@arch-cadre/core 0.0.26 → 0.0.27

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 (35) hide show
  1. package/dist/core/auth/augment.d.cts.map +1 -1
  2. package/dist/core/auth/augment.d.mts.map +1 -1
  3. package/dist/core/auth/augment.mjs.map +1 -1
  4. package/dist/core/auth/email-verification.d.cts +1 -1
  5. package/dist/core/auth/email-verification.d.mts +1 -1
  6. package/dist/core/auth/email-verification.mjs.map +1 -1
  7. package/dist/core/auth/logic.d.cts +6 -6
  8. package/dist/core/auth/logic.d.mts +6 -6
  9. package/dist/core/auth/logic.mjs.map +1 -1
  10. package/dist/core/auth/password-reset.mjs.map +1 -1
  11. package/dist/core/auth/rbac.d.cts +2 -2
  12. package/dist/core/auth/rbac.d.mts +2 -2
  13. package/dist/core/auth/rbac.mjs.map +1 -1
  14. package/dist/core/auth/session.mjs.map +1 -1
  15. package/dist/core/bootstrap.mjs.map +1 -1
  16. package/dist/core/config.server.cjs +1 -1
  17. package/dist/core/config.server.d.cts +1 -1
  18. package/dist/core/config.server.d.mts +1 -1
  19. package/dist/core/config.server.mjs +1 -1
  20. package/dist/core/config.server.mjs.map +1 -1
  21. package/dist/core/event-bus.d.cts.map +1 -1
  22. package/dist/core/event-bus.d.mts.map +1 -1
  23. package/dist/core/event-bus.mjs.map +1 -1
  24. package/dist/core/filesystem/index.mjs.map +1 -1
  25. package/dist/core/filesystem/providers/local.mjs.map +1 -1
  26. package/dist/core/filesystem/service.mjs.map +1 -1
  27. package/dist/core/notifications/actions.d.cts +1 -1
  28. package/dist/core/notifications/actions.d.mts +1 -1
  29. package/dist/core/notifications/actions.mjs.map +1 -1
  30. package/dist/core/notifications/service.mjs.map +1 -1
  31. package/dist/core/setup.mjs.map +1 -1
  32. package/dist/server/auth/email.mjs.map +1 -1
  33. package/dist/server/auth/user.mjs.map +1 -1
  34. package/dist/server/database/inject.mjs.map +1 -1
  35. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"augment.d.cts","names":[],"sources":["../../../src/core/auth/augment.ts"],"mappings":";;;;;AAAgF;KAM3E,iBAAA,IAAqB,IAAA,EAAM,IAAA,KAAS,OAAA,CAAQ,OAAA,CAAQ,QAAA;AAAA,KACpD,gBAAA,IAAoB,OAAA,EAAS,OAAA,KAAY,OAAA,CAAQ,OAAA,CAAQ,OAAA;AAAA,KACzD,6BAAA,IACH,OAAA,EAAS,oBAAA,KACN,OAAA,CAAQ,OAAA,CAAQ,oBAAA;AAAA,iBAuBL,yBAAA,CAA0B,SAAA,EAAW,iBAAA;AAAA,iBAIrC,wBAAA,CAAyB,SAAA,EAAW,gBAAA;AAAA,iBAIpC,qCAAA,CACd,SAAA,EAAW,6BAAA;;;;iBAQS,WAAA,CACpB,IAAA,EAAM,IAAA,EACN,YAAA,GAAe,MAAA,gBACd,OAAA,CAAQ,QAAA;AAAA,iBASW,cAAA,CAAe,OAAA,EAAS,OAAA,GAAU,OAAA,CAAQ,OAAA"}
1
+ {"version":3,"file":"augment.d.cts","names":[],"sources":["../../../src/core/auth/augment.ts"],"mappings":";;;;;AAA6E;KAMxE,iBAAA,IAAqB,IAAA,EAAM,IAAA,KAAS,OAAA,CAAQ,OAAA,CAAQ,QAAA;AAAA,KACpD,gBAAA,IAAoB,OAAA,EAAS,OAAA,KAAY,OAAA,CAAQ,OAAA,CAAQ,OAAA;AAAA,KACzD,6BAAA,IACH,OAAA,EAAS,oBAAA,KACN,OAAA,CAAQ,OAAA,CAAQ,oBAAA;AAAA,iBAuBL,yBAAA,CAA0B,SAAA,EAAW,iBAAA;AAAA,iBAIrC,wBAAA,CAAyB,SAAA,EAAW,gBAAA;AAAA,iBAIpC,qCAAA,CACd,SAAA,EAAW,6BAAA;;;;iBAQS,WAAA,CACpB,IAAA,EAAM,IAAA,EACN,YAAA,GAAe,MAAA,gBACd,OAAA,CAAQ,QAAA;AAAA,iBASW,cAAA,CAAe,OAAA,EAAS,OAAA,GAAU,OAAA,CAAQ,OAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"augment.d.mts","names":[],"sources":["../../../src/core/auth/augment.ts"],"mappings":";;;;;AAAgF;KAM3E,iBAAA,IAAqB,IAAA,EAAM,IAAA,KAAS,OAAA,CAAQ,OAAA,CAAQ,QAAA;AAAA,KACpD,gBAAA,IAAoB,OAAA,EAAS,OAAA,KAAY,OAAA,CAAQ,OAAA,CAAQ,OAAA;AAAA,KACzD,6BAAA,IACH,OAAA,EAAS,oBAAA,KACN,OAAA,CAAQ,OAAA,CAAQ,oBAAA;AAAA,iBAuBL,yBAAA,CAA0B,SAAA,EAAW,iBAAA;AAAA,iBAIrC,wBAAA,CAAyB,SAAA,EAAW,gBAAA;AAAA,iBAIpC,qCAAA,CACd,SAAA,EAAW,6BAAA;;;;iBAQS,WAAA,CACpB,IAAA,EAAM,IAAA,EACN,YAAA,GAAe,MAAA,gBACd,OAAA,CAAQ,QAAA;AAAA,iBASW,cAAA,CAAe,OAAA,EAAS,OAAA,GAAU,OAAA,CAAQ,OAAA"}
1
+ {"version":3,"file":"augment.d.mts","names":[],"sources":["../../../src/core/auth/augment.ts"],"mappings":";;;;;AAA6E;KAMxE,iBAAA,IAAqB,IAAA,EAAM,IAAA,KAAS,OAAA,CAAQ,OAAA,CAAQ,QAAA;AAAA,KACpD,gBAAA,IAAoB,OAAA,EAAS,OAAA,KAAY,OAAA,CAAQ,OAAA,CAAQ,OAAA;AAAA,KACzD,6BAAA,IACH,OAAA,EAAS,oBAAA,KACN,OAAA,CAAQ,OAAA,CAAQ,oBAAA;AAAA,iBAuBL,yBAAA,CAA0B,SAAA,EAAW,iBAAA;AAAA,iBAIrC,wBAAA,CAAyB,SAAA,EAAW,gBAAA;AAAA,iBAIpC,qCAAA,CACd,SAAA,EAAW,6BAAA;;;;iBAQS,WAAA,CACpB,IAAA,EAAM,IAAA,EACN,YAAA,GAAe,MAAA,gBACd,OAAA,CAAQ,QAAA;AAAA,iBASW,cAAA,CAAe,OAAA,EAAS,OAAA,GAAU,OAAA,CAAQ,OAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"augment.mjs","names":[],"sources":["../../../src/core/auth/augment.ts"],"sourcesContent":["import type { FullUser, PasswordResetSession, Session, User } from \"./types.js\";\n\n/**\n * REGISTRIES FOR MODULAR EXTENSIONS\n */\n\ntype IdentityAugmenter = (user: User) => Promise<Partial<FullUser>>;\ntype SessionAugmenter = (session: Session) => Promise<Partial<Session>>;\ntype PasswordResetSessionAugmenter = (\n session: PasswordResetSession,\n) => Promise<Partial<PasswordResetSession>>;\n\nconst globalForAugment = globalThis as unknown as {\n __KRYO_IDENTITY_AUGMENTERS__: Set<IdentityAugmenter> | undefined;\n __KRYO_SESSION_AUGMENTERS__: Set<SessionAugmenter> | undefined;\n __KRYO_PASSWORD_RESET_SESSION_AUGMENTERS__:\n | Set<PasswordResetSessionAugmenter>\n | undefined;\n};\n\nconst identityAugmenters =\n globalForAugment.__KRYO_IDENTITY_AUGMENTERS__ ?? new Set<IdentityAugmenter>();\nconst sessionAugmenters =\n globalForAugment.__KRYO_SESSION_AUGMENTERS__ ?? new Set<SessionAugmenter>();\nconst passwordResetSessionAugmenters =\n globalForAugment.__KRYO_PASSWORD_RESET_SESSION_AUGMENTERS__ ??\n new Set<PasswordResetSessionAugmenter>();\n\nglobalForAugment.__KRYO_IDENTITY_AUGMENTERS__ = identityAugmenters;\nglobalForAugment.__KRYO_SESSION_AUGMENTERS__ = sessionAugmenters;\nglobalForAugment.__KRYO_PASSWORD_RESET_SESSION_AUGMENTERS__ =\n passwordResetSessionAugmenters;\n\nexport function registerIdentityAugmenter(augmenter: IdentityAugmenter) {\n identityAugmenters.add(augmenter);\n}\n\nexport function registerSessionAugmenter(augmenter: SessionAugmenter) {\n sessionAugmenters.add(augmenter);\n}\n\nexport function registerPasswordResetSessionAugmenter(\n augmenter: PasswordResetSessionAugmenter,\n) {\n passwordResetSessionAugmenters.add(augmenter);\n}\n\n/**\n * EXECUTION FUNCTIONS\n */\nexport async function augmentUser(\n user: User,\n coreRbacData?: Record<string, any>,\n): Promise<FullUser> {\n let augmentedData = coreRbacData || {};\n for (const augmenter of identityAugmenters) {\n const data = await augmenter(user);\n augmentedData = { ...augmentedData, ...data };\n }\n return { ...user, ...augmentedData } as FullUser;\n}\n\nexport async function augmentSession(session: Session): Promise<Session> {\n let augmentedData = {};\n for (const augmenter of sessionAugmenters) {\n const data = await augmenter(session);\n augmentedData = { ...augmentedData, ...data };\n }\n return { ...session, ...augmentedData } as Session;\n}\n\nexport async function augmentPasswordResetSession(\n session: PasswordResetSession,\n): Promise<PasswordResetSession> {\n let augmentedData = {};\n for (const augmenter of passwordResetSessionAugmenters) {\n const data = await augmenter(session);\n augmentedData = { ...augmentedData, ...data };\n }\n return { ...session, ...augmentedData } as PasswordResetSession;\n}\n"],"mappings":"AAYA,MAAM,EAAmB,WAQnB,EACJ,EAAiB,8BAAgC,IAAI,IACjD,EACJ,EAAiB,6BAA+B,IAAI,IAChD,EACJ,EAAiB,4CACjB,IAAI,IAEN,EAAiB,6BAA+B,EAChD,EAAiB,4BAA8B,EAC/C,EAAiB,2CACf,EAEF,SAAgB,EAA0B,EAA8B,CACtE,EAAmB,IAAI,EAAU,CAGnC,SAAgB,EAAyB,EAA6B,CACpE,EAAkB,IAAI,EAAU,CAGlC,SAAgB,EACd,EACA,CACA,EAA+B,IAAI,EAAU,CAM/C,eAAsB,EACpB,EACA,EACmB,CACnB,IAAI,EAAgB,GAAgB,EAAE,CACtC,IAAK,IAAM,KAAa,EAAoB,CAC1C,IAAM,EAAO,MAAM,EAAU,EAAK,CAClC,EAAgB,CAAE,GAAG,EAAe,GAAG,EAAM,CAE/C,MAAO,CAAE,GAAG,EAAM,GAAG,EAAe,CAGtC,eAAsB,EAAe,EAAoC,CACvE,IAAI,EAAgB,EAAE,CACtB,IAAK,IAAM,KAAa,EAAmB,CACzC,IAAM,EAAO,MAAM,EAAU,EAAQ,CACrC,EAAgB,CAAE,GAAG,EAAe,GAAG,EAAM,CAE/C,MAAO,CAAE,GAAG,EAAS,GAAG,EAAe,CAGzC,eAAsB,EACpB,EAC+B,CAC/B,IAAI,EAAgB,EAAE,CACtB,IAAK,IAAM,KAAa,EAAgC,CACtD,IAAM,EAAO,MAAM,EAAU,EAAQ,CACrC,EAAgB,CAAE,GAAG,EAAe,GAAG,EAAM,CAE/C,MAAO,CAAE,GAAG,EAAS,GAAG,EAAe"}
1
+ {"version":3,"file":"augment.mjs","names":[],"sources":["../../../src/core/auth/augment.ts"],"sourcesContent":["import type { FullUser, PasswordResetSession, Session, User } from \"./types\";\n\n/**\n * REGISTRIES FOR MODULAR EXTENSIONS\n */\n\ntype IdentityAugmenter = (user: User) => Promise<Partial<FullUser>>;\ntype SessionAugmenter = (session: Session) => Promise<Partial<Session>>;\ntype PasswordResetSessionAugmenter = (\n session: PasswordResetSession,\n) => Promise<Partial<PasswordResetSession>>;\n\nconst globalForAugment = globalThis as unknown as {\n __KRYO_IDENTITY_AUGMENTERS__: Set<IdentityAugmenter> | undefined;\n __KRYO_SESSION_AUGMENTERS__: Set<SessionAugmenter> | undefined;\n __KRYO_PASSWORD_RESET_SESSION_AUGMENTERS__:\n | Set<PasswordResetSessionAugmenter>\n | undefined;\n};\n\nconst identityAugmenters =\n globalForAugment.__KRYO_IDENTITY_AUGMENTERS__ ?? new Set<IdentityAugmenter>();\nconst sessionAugmenters =\n globalForAugment.__KRYO_SESSION_AUGMENTERS__ ?? new Set<SessionAugmenter>();\nconst passwordResetSessionAugmenters =\n globalForAugment.__KRYO_PASSWORD_RESET_SESSION_AUGMENTERS__ ??\n new Set<PasswordResetSessionAugmenter>();\n\nglobalForAugment.__KRYO_IDENTITY_AUGMENTERS__ = identityAugmenters;\nglobalForAugment.__KRYO_SESSION_AUGMENTERS__ = sessionAugmenters;\nglobalForAugment.__KRYO_PASSWORD_RESET_SESSION_AUGMENTERS__ =\n passwordResetSessionAugmenters;\n\nexport function registerIdentityAugmenter(augmenter: IdentityAugmenter) {\n identityAugmenters.add(augmenter);\n}\n\nexport function registerSessionAugmenter(augmenter: SessionAugmenter) {\n sessionAugmenters.add(augmenter);\n}\n\nexport function registerPasswordResetSessionAugmenter(\n augmenter: PasswordResetSessionAugmenter,\n) {\n passwordResetSessionAugmenters.add(augmenter);\n}\n\n/**\n * EXECUTION FUNCTIONS\n */\nexport async function augmentUser(\n user: User,\n coreRbacData?: Record<string, any>,\n): Promise<FullUser> {\n let augmentedData = coreRbacData || {};\n for (const augmenter of identityAugmenters) {\n const data = await augmenter(user);\n augmentedData = { ...augmentedData, ...data };\n }\n return { ...user, ...augmentedData } as FullUser;\n}\n\nexport async function augmentSession(session: Session): Promise<Session> {\n let augmentedData = {};\n for (const augmenter of sessionAugmenters) {\n const data = await augmenter(session);\n augmentedData = { ...augmentedData, ...data };\n }\n return { ...session, ...augmentedData } as Session;\n}\n\nexport async function augmentPasswordResetSession(\n session: PasswordResetSession,\n): Promise<PasswordResetSession> {\n let augmentedData = {};\n for (const augmenter of passwordResetSessionAugmenters) {\n const data = await augmenter(session);\n augmentedData = { ...augmentedData, ...data };\n }\n return { ...session, ...augmentedData } as PasswordResetSession;\n}\n"],"mappings":"AAYA,MAAM,EAAmB,WAQnB,EACJ,EAAiB,8BAAgC,IAAI,IACjD,EACJ,EAAiB,6BAA+B,IAAI,IAChD,EACJ,EAAiB,4CACjB,IAAI,IAEN,EAAiB,6BAA+B,EAChD,EAAiB,4BAA8B,EAC/C,EAAiB,2CACf,EAEF,SAAgB,EAA0B,EAA8B,CACtE,EAAmB,IAAI,EAAU,CAGnC,SAAgB,EAAyB,EAA6B,CACpE,EAAkB,IAAI,EAAU,CAGlC,SAAgB,EACd,EACA,CACA,EAA+B,IAAI,EAAU,CAM/C,eAAsB,EACpB,EACA,EACmB,CACnB,IAAI,EAAgB,GAAgB,EAAE,CACtC,IAAK,IAAM,KAAa,EAAoB,CAC1C,IAAM,EAAO,MAAM,EAAU,EAAK,CAClC,EAAgB,CAAE,GAAG,EAAe,GAAG,EAAM,CAE/C,MAAO,CAAE,GAAG,EAAM,GAAG,EAAe,CAGtC,eAAsB,EAAe,EAAoC,CACvE,IAAI,EAAgB,EAAE,CACtB,IAAK,IAAM,KAAa,EAAmB,CACzC,IAAM,EAAO,MAAM,EAAU,EAAQ,CACrC,EAAgB,CAAE,GAAG,EAAe,GAAG,EAAM,CAE/C,MAAO,CAAE,GAAG,EAAS,GAAG,EAAe,CAGzC,eAAsB,EACpB,EAC+B,CAC/B,IAAI,EAAgB,EAAE,CACtB,IAAK,IAAM,KAAa,EAAgC,CACtD,IAAM,EAAO,MAAM,EAAU,EAAQ,CACrC,EAAgB,CAAE,GAAG,EAAe,GAAG,EAAM,CAE/C,MAAO,CAAE,GAAG,EAAS,GAAG,EAAe"}
@@ -21,9 +21,9 @@ declare function getUserEmailVerificationRequest(userId: string, id: string): Pr
21
21
  * Creates a new email verification request, deleting any existing one for the user.
22
22
  */
23
23
  declare function createEmailVerificationRequest(userId: string, email: string): Promise<{
24
+ id: string;
24
25
  email: string;
25
26
  code: string;
26
- id: string;
27
27
  createdAt: Date;
28
28
  updatedAt: Date | null;
29
29
  userId: string;
@@ -21,9 +21,9 @@ declare function getUserEmailVerificationRequest(userId: string, id: string): Pr
21
21
  * Creates a new email verification request, deleting any existing one for the user.
22
22
  */
23
23
  declare function createEmailVerificationRequest(userId: string, email: string): Promise<{
24
+ id: string;
24
25
  email: string;
25
26
  code: string;
26
- id: string;
27
27
  createdAt: Date;
28
28
  updatedAt: Date | null;
29
29
  userId: string;
@@ -1 +1 @@
1
- {"version":3,"file":"email-verification.mjs","names":[],"sources":["../../../src/core/auth/email-verification.ts"],"sourcesContent":["\"use server\";\n\nimport { addHours } from \"date-fns\";\nimport { and, eq } from \"drizzle-orm\";\nimport { cookies } from \"next/headers\";\nimport { db } from \"../../server/database/inject.js\";\nimport { emailVerificationTable } from \"../../server/database/schema.js\";\nimport { sendVerifyEmail } from \"../../server/emails/index.js\";\nimport { registerSecurityRequirement } from \"./logic.js\";\nimport { getCurrentSession } from \"./session.js\";\nimport { generateRandomOTP } from \"./utils/encode.js\";\n\n/**\n * Register Email Verification as a Core Security Requirement.\n */\nexport async function initEmailVerification() {\n registerSecurityRequirement(async (_session, user) => {\n if (!user.emailVerifiedAt) {\n return {\n satisfied: false,\n redirect: \"/verify-email?unverified\",\n };\n }\n return { satisfied: true };\n });\n}\n\n/**\n * Retrieves a specific email verification request for a user.\n */\nexport async function getUserEmailVerificationRequest(\n userId: string,\n id: string,\n) {\n const [session] = await db\n .select()\n .from(emailVerificationTable)\n .where(\n and(\n eq(emailVerificationTable.id, id),\n eq(emailVerificationTable.userId, userId),\n ),\n );\n\n return session;\n}\n\n/**\n * Creates a new email verification request, deleting any existing one for the user.\n */\nexport async function createEmailVerificationRequest(\n userId: string,\n email: string,\n) {\n await deleteUserEmailVerificationRequest(userId);\n\n const code = generateRandomOTP();\n\n const [verificationRequest] = await db\n .insert(emailVerificationTable)\n .values({\n userId,\n code,\n email,\n expiresAt: new Date(addHours(new Date(), 1)),\n })\n .returning();\n\n return verificationRequest;\n}\n\n/**\n * Deletes all email verification requests for a user.\n */\nexport async function deleteUserEmailVerificationRequest(\n userId: string,\n): Promise<void> {\n await db\n .delete(emailVerificationTable)\n .where(eq(emailVerificationTable.userId, userId));\n}\n\n/**\n * Sends a verification email with the OTP code.\n */\nexport async function sendVerificationEmail(\n email: string,\n code: string,\n): Promise<void> {\n await sendVerifyEmail(email, code);\n}\n\n/**\n * Sets the email verification request ID in a cookie.\n */\nexport async function setEmailVerificationRequestCookie(\n request: typeof emailVerificationTable.$inferSelect,\n): Promise<void> {\n const cookieStore = await cookies();\n\n cookieStore.set(\"email_verification\", request.id, {\n httpOnly: true,\n path: \"/\",\n secure: process.env.NODE_ENV === \"production\",\n sameSite: \"lax\",\n expires: request.expiresAt,\n });\n}\n\n/**\n * Removes the email verification request cookie.\n */\nexport async function deleteEmailVerificationRequestCookie(): Promise<void> {\n const cookieStore = await cookies();\n cookieStore.delete(\"email_verification\");\n}\n\n/**\n * Retrieves the current email verification request based on session and cookie.\n */\nexport async function getUserEmailVerificationRequestFromRequest() {\n const { user } = await getCurrentSession();\n\n if (!user) {\n return null;\n }\n\n const cookieStore = await cookies();\n const id = cookieStore.get(\"email_verification\")?.value ?? null;\n\n if (!id) {\n return null;\n }\n\n const request = await getUserEmailVerificationRequest(user.id, id);\n\n if (!request) {\n await deleteEmailVerificationRequestCookie();\n }\n\n return request;\n}\n"],"mappings":"qeAeA,eAAsB,GAAwB,CAC5C,EAA4B,MAAO,EAAU,IACtC,EAAK,gBAMH,CAAE,UAAW,GAAM,CALjB,CACL,UAAW,GACX,SAAU,2BACX,CAGH,CAMJ,eAAsB,EACpB,EACA,EACA,CACA,GAAM,CAAC,GAAW,MAAM,EACrB,QAAQ,CACR,KAAK,EAAuB,CAC5B,MACC,EACE,EAAG,EAAuB,GAAI,EAAG,CACjC,EAAG,EAAuB,OAAQ,EAAO,CAC1C,CACF,CAEH,OAAO,EAMT,eAAsB,EACpB,EACA,EACA,CACA,MAAM,EAAmC,EAAO,CAEhD,IAAM,EAAO,GAAmB,CAE1B,CAAC,GAAuB,MAAM,EACjC,OAAO,EAAuB,CAC9B,OAAO,CACN,SACA,OACA,QACA,UAAW,IAAI,KAAK,EAAS,IAAI,KAAQ,EAAE,CAAC,CAC7C,CAAC,CACD,WAAW,CAEd,OAAO,EAMT,eAAsB,EACpB,EACe,CACf,MAAM,EACH,OAAO,EAAuB,CAC9B,MAAM,EAAG,EAAuB,OAAQ,EAAO,CAAC,CAMrD,eAAsB,EACpB,EACA,EACe,CACf,MAAM,EAAgB,EAAO,EAAK,CAMpC,eAAsB,EACpB,EACe,EACK,MAAM,GAAS,EAEvB,IAAI,qBAAsB,EAAQ,GAAI,CAChD,SAAU,GACV,KAAM,IACN,OAAQ,QAAQ,IAAI,WAAa,aACjC,SAAU,MACV,QAAS,EAAQ,UAClB,CAAC,CAMJ,eAAsB,GAAsD,EACtD,MAAM,GAAS,EACvB,OAAO,qBAAqB,CAM1C,eAAsB,GAA6C,CACjE,GAAM,CAAE,QAAS,MAAM,GAAmB,CAE1C,GAAI,CAAC,EACH,OAAO,KAIT,IAAM,GADc,MAAM,GAAS,EACZ,IAAI,qBAAqB,EAAE,OAAS,KAE3D,GAAI,CAAC,EACH,OAAO,KAGT,IAAM,EAAU,MAAM,EAAgC,EAAK,GAAI,EAAG,CAMlE,OAJK,GACH,MAAM,GAAsC,CAGvC"}
1
+ {"version":3,"file":"email-verification.mjs","names":[],"sources":["../../../src/core/auth/email-verification.ts"],"sourcesContent":["\"use server\";\n\nimport { addHours } from \"date-fns\";\nimport { and, eq } from \"drizzle-orm\";\nimport { cookies } from \"next/headers\";\nimport { db } from \"../../server/database/inject\";\nimport { emailVerificationTable } from \"../../server/database/schema\";\nimport { sendVerifyEmail } from \"../../server/emails/index\";\nimport { registerSecurityRequirement } from \"./logic\";\nimport { getCurrentSession } from \"./session\";\nimport { generateRandomOTP } from \"./utils/encode\";\n\n/**\n * Register Email Verification as a Core Security Requirement.\n */\nexport async function initEmailVerification() {\n registerSecurityRequirement(async (_session, user) => {\n if (!user.emailVerifiedAt) {\n return {\n satisfied: false,\n redirect: \"/verify-email?unverified\",\n };\n }\n return { satisfied: true };\n });\n}\n\n/**\n * Retrieves a specific email verification request for a user.\n */\nexport async function getUserEmailVerificationRequest(\n userId: string,\n id: string,\n) {\n const [session] = await db\n .select()\n .from(emailVerificationTable)\n .where(\n and(\n eq(emailVerificationTable.id, id),\n eq(emailVerificationTable.userId, userId),\n ),\n );\n\n return session;\n}\n\n/**\n * Creates a new email verification request, deleting any existing one for the user.\n */\nexport async function createEmailVerificationRequest(\n userId: string,\n email: string,\n) {\n await deleteUserEmailVerificationRequest(userId);\n\n const code = generateRandomOTP();\n\n const [verificationRequest] = await db\n .insert(emailVerificationTable)\n .values({\n userId,\n code,\n email,\n expiresAt: new Date(addHours(new Date(), 1)),\n })\n .returning();\n\n return verificationRequest;\n}\n\n/**\n * Deletes all email verification requests for a user.\n */\nexport async function deleteUserEmailVerificationRequest(\n userId: string,\n): Promise<void> {\n await db\n .delete(emailVerificationTable)\n .where(eq(emailVerificationTable.userId, userId));\n}\n\n/**\n * Sends a verification email with the OTP code.\n */\nexport async function sendVerificationEmail(\n email: string,\n code: string,\n): Promise<void> {\n await sendVerifyEmail(email, code);\n}\n\n/**\n * Sets the email verification request ID in a cookie.\n */\nexport async function setEmailVerificationRequestCookie(\n request: typeof emailVerificationTable.$inferSelect,\n): Promise<void> {\n const cookieStore = await cookies();\n\n cookieStore.set(\"email_verification\", request.id, {\n httpOnly: true,\n path: \"/\",\n secure: process.env.NODE_ENV === \"production\",\n sameSite: \"lax\",\n expires: request.expiresAt,\n });\n}\n\n/**\n * Removes the email verification request cookie.\n */\nexport async function deleteEmailVerificationRequestCookie(): Promise<void> {\n const cookieStore = await cookies();\n cookieStore.delete(\"email_verification\");\n}\n\n/**\n * Retrieves the current email verification request based on session and cookie.\n */\nexport async function getUserEmailVerificationRequestFromRequest() {\n const { user } = await getCurrentSession();\n\n if (!user) {\n return null;\n }\n\n const cookieStore = await cookies();\n const id = cookieStore.get(\"email_verification\")?.value ?? null;\n\n if (!id) {\n return null;\n }\n\n const request = await getUserEmailVerificationRequest(user.id, id);\n\n if (!request) {\n await deleteEmailVerificationRequestCookie();\n }\n\n return request;\n}\n"],"mappings":"qeAeA,eAAsB,GAAwB,CAC5C,EAA4B,MAAO,EAAU,IACtC,EAAK,gBAMH,CAAE,UAAW,GAAM,CALjB,CACL,UAAW,GACX,SAAU,2BACX,CAGH,CAMJ,eAAsB,EACpB,EACA,EACA,CACA,GAAM,CAAC,GAAW,MAAM,EACrB,QAAQ,CACR,KAAK,EAAuB,CAC5B,MACC,EACE,EAAG,EAAuB,GAAI,EAAG,CACjC,EAAG,EAAuB,OAAQ,EAAO,CAC1C,CACF,CAEH,OAAO,EAMT,eAAsB,EACpB,EACA,EACA,CACA,MAAM,EAAmC,EAAO,CAEhD,IAAM,EAAO,GAAmB,CAE1B,CAAC,GAAuB,MAAM,EACjC,OAAO,EAAuB,CAC9B,OAAO,CACN,SACA,OACA,QACA,UAAW,IAAI,KAAK,EAAS,IAAI,KAAQ,EAAE,CAAC,CAC7C,CAAC,CACD,WAAW,CAEd,OAAO,EAMT,eAAsB,EACpB,EACe,CACf,MAAM,EACH,OAAO,EAAuB,CAC9B,MAAM,EAAG,EAAuB,OAAQ,EAAO,CAAC,CAMrD,eAAsB,EACpB,EACA,EACe,CACf,MAAM,EAAgB,EAAO,EAAK,CAMpC,eAAsB,EACpB,EACe,EACK,MAAM,GAAS,EAEvB,IAAI,qBAAsB,EAAQ,GAAI,CAChD,SAAU,GACV,KAAM,IACN,OAAQ,QAAQ,IAAI,WAAa,aACjC,SAAU,MACV,QAAS,EAAQ,UAClB,CAAC,CAMJ,eAAsB,GAAsD,EACtD,MAAM,GAAS,EACvB,OAAO,qBAAqB,CAM1C,eAAsB,GAA6C,CACjE,GAAM,CAAE,QAAS,MAAM,GAAmB,CAE1C,GAAI,CAAC,EACH,OAAO,KAIT,IAAM,GADc,MAAM,GAAS,EACZ,IAAI,qBAAqB,EAAE,OAAS,KAE3D,GAAI,CAAC,EACH,OAAO,KAGT,IAAM,EAAU,MAAM,EAAgC,EAAK,GAAI,EAAG,CAMlE,OAJK,GACH,MAAM,GAAsC,CAGvC"}
@@ -58,15 +58,15 @@ declare function signUp(data: RegisterInput): Promise<{
58
58
  createdAt: Date;
59
59
  updatedAt: Date | null;
60
60
  userId: string;
61
- active_organization_id: string | null;
62
61
  expiresAt: Date;
62
+ active_organization_id: string | null;
63
63
  };
64
64
  user: {
65
65
  [x: string]: any;
66
+ id: string;
66
67
  email: string;
67
- password: string | null;
68
68
  name: string;
69
- id: string;
69
+ password: string | null;
70
70
  image: string | null;
71
71
  recovery_code: Buffer<ArrayBufferLike>;
72
72
  emailVerifiedAt: Date | null;
@@ -86,14 +86,14 @@ declare function finalizeLogin(userId: string, flags: SessionFlags): Promise<{
86
86
  createdAt: Date;
87
87
  updatedAt: Date | null;
88
88
  userId: string;
89
- active_organization_id: string | null;
90
89
  expiresAt: Date;
90
+ active_organization_id: string | null;
91
91
  } | null;
92
92
  user: {
93
+ id: string;
93
94
  email: string;
94
- password: string | null;
95
95
  name: string;
96
- id: string;
96
+ password: string | null;
97
97
  image: string | null;
98
98
  recovery_code: Buffer<ArrayBufferLike>;
99
99
  emailVerifiedAt: Date | null;
@@ -58,15 +58,15 @@ declare function signUp(data: RegisterInput): Promise<{
58
58
  createdAt: Date;
59
59
  updatedAt: Date | null;
60
60
  userId: string;
61
- active_organization_id: string | null;
62
61
  expiresAt: Date;
62
+ active_organization_id: string | null;
63
63
  };
64
64
  user: {
65
65
  [x: string]: any;
66
+ id: string;
66
67
  email: string;
67
- password: string | null;
68
68
  name: string;
69
- id: string;
69
+ password: string | null;
70
70
  image: string | null;
71
71
  recovery_code: Buffer<ArrayBufferLike>;
72
72
  emailVerifiedAt: Date | null;
@@ -86,14 +86,14 @@ declare function finalizeLogin(userId: string, flags: SessionFlags): Promise<{
86
86
  createdAt: Date;
87
87
  updatedAt: Date | null;
88
88
  userId: string;
89
- active_organization_id: string | null;
90
89
  expiresAt: Date;
90
+ active_organization_id: string | null;
91
91
  } | null;
92
92
  user: {
93
+ id: string;
93
94
  email: string;
94
- password: string | null;
95
95
  name: string;
96
- id: string;
96
+ password: string | null;
97
97
  image: string | null;
98
98
  recovery_code: Buffer<ArrayBufferLike>;
99
99
  emailVerifiedAt: Date | null;
@@ -1 +1 @@
1
- {"version":3,"file":"logic.mjs","names":[],"sources":["../../../src/core/auth/logic.ts"],"sourcesContent":["\"use server\";\n\nimport { eq, inArray } from \"drizzle-orm\";\nimport {\n verifyPasswordHash,\n verifyPasswordStrength,\n} from \"../../server/auth/password.js\";\nimport {\n createUser,\n getUserById,\n getUserFromEmail,\n getUserPasswordHash,\n verifyUsernameInput,\n} from \"../../server/auth/user.js\";\nimport { db } from \"../../server/database/inject.js\";\nimport {\n permissionsTable,\n rolesTable,\n rolesToPermissionsTable,\n usersToPermissionsTable,\n usersToRolesTable,\n} from \"../../server/database/schema.js\";\nimport { eventBus } from \"../event-bus.js\";\nimport {\n augmentSession,\n augmentUser,\n registerIdentityAugmenter,\n registerPasswordResetSessionAugmenter,\n registerSessionAugmenter,\n} from \"./augment.js\";\nimport {\n createEmailVerificationRequest,\n sendVerificationEmail,\n setEmailVerificationRequestCookie,\n} from \"./email-verification.js\";\nimport {\n createSession,\n deleteSessionTokenCookie,\n generateSessionToken,\n getCurrentSession,\n invalidateSession,\n setSessionTokenCookie,\n} from \"./session.js\";\nimport type {\n AuthResponse,\n FullUser,\n Session,\n SessionFlags,\n User,\n UserPermission,\n UserRole,\n} from \"./types.js\";\nimport {\n type LoginInput,\n loginSchema,\n type RegisterInput,\n registerSchema,\n} from \"./validation.js\";\n\n/**\n * Podstawowy moduł rozszerzający tożsamość dla ról i uprawnień\n */\nasync function coreRbacAugmenter(user: User): Promise<Record<string, any>> {\n try {\n // 1. Fetch direct roles\n const userRoles = await db\n .select({ name: rolesTable.name })\n .from(usersToRolesTable)\n .innerJoin(rolesTable, eq(usersToRolesTable.roleId, rolesTable.id))\n .where(eq(usersToRolesTable.userId, user.id));\n\n const roles = userRoles.map((r) => r.name);\n\n // 2. Fetch direct permissions\n const userDirectPerms = await db\n .select({ name: permissionsTable.name })\n .from(usersToPermissionsTable)\n .innerJoin(\n permissionsTable,\n eq(usersToPermissionsTable.permissionId, permissionsTable.id),\n )\n .where(eq(usersToPermissionsTable.userId, user.id));\n\n const directPerms = userDirectPerms.map((p) => p.name);\n\n // 3. Fetch permissions from roles\n let rolePerms: string[] = [];\n if (roles.length > 0) {\n const roleIdsResult = await db\n .select({ id: rolesTable.id })\n .from(rolesTable)\n .where(inArray(rolesTable.name, roles));\n\n const roleIds = roleIdsResult.map((r) => r.id);\n\n if (roleIds.length > 0) {\n const rolePermsData = await db\n .select({ name: permissionsTable.name })\n .from(rolesToPermissionsTable)\n .innerJoin(\n permissionsTable,\n eq(rolesToPermissionsTable.permissionId, permissionsTable.id),\n )\n .where(inArray(rolesToPermissionsTable.roleId, roleIds));\n rolePerms = rolePermsData.map((p) => p.name);\n }\n }\n\n return {\n roles,\n permissions: Array.from(new Set([...directPerms, ...rolePerms])),\n };\n } catch (error) {\n console.error(\"[Auth:RBAC] Failed to augment user:\", error);\n return { roles: [], permissions: [] };\n }\n}\n\n/**\n * Registry for login validators (e.g. 2FA module)\n */\ntype AuthValidator = (userId: string) => Promise<AuthResponse | null>;\n\n/**\n * Registry for Security Requirements (e.g. checking if 2FA is needed for a session)\n */\ntype SecurityRequirement = (\n session: Session,\n user: FullUser,\n) => Promise<{ satisfied: boolean; redirect?: string } | null>;\n\n/**\n * Registry for password reset validators (e.g. 2FA module requiring check during reset)\n */\ntype PasswordResetValidator = (userId: string) => Promise<AuthResponse | null>;\n\n/**\n * Registry for email verification validators\n */\ntype EmailVerificationValidator = (\n userId: string,\n) => Promise<AuthResponse | null>;\n\nconst globalForAuth = globalThis as unknown as {\n __KRYO_AUTH_VALIDATORS__: Set<AuthValidator> | undefined;\n __KRYO_SECURITY_REQUIREMENTS__: Set<SecurityRequirement> | undefined;\n __KRYO_PASSWORD_RESET_VALIDATORS__: Set<PasswordResetValidator> | undefined;\n __KRYO_EMAIL_VERIFICATION_VALIDATORS__:\n | Set<EmailVerificationValidator>\n | undefined;\n};\n\nconst authValidators =\n globalForAuth.__KRYO_AUTH_VALIDATORS__ ?? new Set<AuthValidator>();\nconst securityRequirements =\n globalForAuth.__KRYO_SECURITY_REQUIREMENTS__ ??\n new Set<SecurityRequirement>();\nconst passwordResetValidators =\n globalForAuth.__KRYO_PASSWORD_RESET_VALIDATORS__ ??\n new Set<PasswordResetValidator>();\nconst emailVerificationValidators =\n globalForAuth.__KRYO_EMAIL_VERIFICATION_VALIDATORS__ ??\n new Set<EmailVerificationValidator>();\n\nglobalForAuth.__KRYO_AUTH_VALIDATORS__ = authValidators;\nglobalForAuth.__KRYO_SECURITY_REQUIREMENTS__ = securityRequirements;\nglobalForAuth.__KRYO_PASSWORD_RESET_VALIDATORS__ = passwordResetValidators;\nglobalForAuth.__KRYO_EMAIL_VERIFICATION_VALIDATORS__ =\n emailVerificationValidators;\n\nexport async function registerAuthValidator(validator: AuthValidator) {\n authValidators.add(validator);\n}\n\nexport async function registerPasswordResetValidator(\n validator: PasswordResetValidator,\n) {\n passwordResetValidators.add(validator);\n}\n\nexport async function registerEmailVerificationValidator(\n validator: EmailVerificationValidator,\n) {\n emailVerificationValidators.add(validator);\n}\n\nexport {\n registerIdentityAugmenter,\n registerSessionAugmenter,\n registerPasswordResetSessionAugmenter,\n augmentUser,\n augmentSession,\n};\n\nexport async function registerSecurityRequirement(\n requirement: SecurityRequirement,\n) {\n securityRequirements.add(requirement);\n}\n\nexport async function runPasswordResetValidators(\n userId: string,\n): Promise<AuthResponse | null> {\n for (const validator of passwordResetValidators) {\n const interception = await validator(userId);\n if (interception) return interception;\n }\n return null;\n}\n\nexport async function runEmailVerificationValidators(\n userId: string,\n): Promise<AuthResponse | null> {\n for (const validator of emailVerificationValidators) {\n const interception = await validator(userId);\n if (interception) return interception;\n }\n return null;\n}\n\n/**\n * Augments a base user with data from all registered modules.\n * This is now just a wrapper that includes core RBAC data.\n */\nexport async function performFullUserAugmentation(\n user: User,\n): Promise<FullUser> {\n const coreRbacData = await coreRbacAugmenter(user);\n return await augmentUser(user, coreRbacData);\n}\n\n/**\n * Checks if the current session satisfies all registered security requirements.\n */\nexport async function checkSecurity(\n session: Session,\n user: FullUser,\n requiredRoles?: UserRole[],\n requiredPermissions?: UserPermission[],\n fallbackRedirect?: string,\n) {\n if (!user) {\n console.warn(\"User is required for security check\");\n return { satisfied: false, redirect: fallbackRedirect ?? \"/signin\" };\n }\n\n const userRoles = Array.isArray(user.roles) ? user.roles : [];\n const userPermissions = Array.isArray(user.permissions)\n ? user.permissions\n : [];\n\n // 1. Core Role Check (At least one role must match)\n if (requiredRoles && requiredRoles.length > 0) {\n const hasRole = requiredRoles.some((role) => userRoles.includes(role));\n if (!hasRole) {\n console.warn(`User lacks required roles: ${requiredRoles.join(\", \")}`);\n return {\n satisfied: false,\n redirect: fallbackRedirect,\n };\n }\n }\n\n // 2. Core Permission Check (ALL permissions must match)\n if (requiredPermissions && requiredPermissions.length > 0) {\n const hasAllPermissions = requiredPermissions.every((perm) =>\n userPermissions.includes(perm),\n );\n if (!hasAllPermissions) {\n console.warn(\n `User lacks required permissions: ${requiredPermissions.join(\", \")}`,\n );\n\n return {\n satisfied: false,\n redirect: fallbackRedirect,\n };\n }\n }\n\n // 3. Modular Requirements Check\n if (securityRequirements) {\n for (const requirement of securityRequirements) {\n try {\n const result = await requirement(session, user);\n if (result && !result.satisfied) {\n return {\n ...result,\n redirect: result.redirect ?? fallbackRedirect,\n };\n }\n } catch (error) {\n console.error(\"[Auth:Security] Requirement failed:\", error);\n }\n }\n }\n return { satisfied: true };\n}\n\n/**\n * Sign In Logic\n */\nexport async function signIn(data: LoginInput): Promise<AuthResponse> {\n const { email, password } = await loginSchema.parseAsync(data);\n\n const user = await getUserFromEmail(email);\n if (!user) {\n return { status: \"ERROR\", message: \"Invalid email or password\" };\n }\n\n const passwordHash = await getUserPasswordHash(user.id);\n if (!passwordHash || !(await verifyPasswordHash(passwordHash, password))) {\n return { status: \"ERROR\", message: \"Invalid email or password\" };\n }\n\n // Interception Layer\n for (const validator of authValidators) {\n const interception = await validator(user.id);\n if (interception) return interception;\n }\n\n const sessionFlags: SessionFlags = {};\n const sessionToken = await generateSessionToken();\n const session = await createSession(sessionToken, user.id, sessionFlags);\n await setSessionTokenCookie(sessionToken, session.expiresAt);\n\n const fullUser = await performFullUserAugmentation(user);\n await eventBus.publish(\"auth:session-created\", { session, user: fullUser });\n\n return {\n status: \"SUCCESS\",\n session: { ...session },\n user: { ...fullUser },\n };\n}\n\n/**\n * Sign Up Logic\n */\nexport async function signUp(data: RegisterInput) {\n const { email, username, password } = registerSchema.parse(data);\n\n if (!(await verifyUsernameInput(username))) {\n throw new Error(\"Invalid username\");\n }\n\n if (!(await verifyPasswordStrength(password))) {\n throw new Error(\"Weak password\");\n }\n\n const user = await createUser(email, username, password);\n const verificationRequest = await createEmailVerificationRequest(\n user.id,\n user.email,\n );\n\n await sendVerificationEmail(\n verificationRequest.email,\n verificationRequest.code,\n );\n await setEmailVerificationRequestCookie(verificationRequest);\n\n const sessionFlags: SessionFlags = {};\n const sessionToken = await generateSessionToken();\n const session = await createSession(sessionToken, user.id, sessionFlags);\n await setSessionTokenCookie(sessionToken, session.expiresAt);\n\n const fullUser = await performFullUserAugmentation(user);\n await eventBus.publish(\"auth:session-created\", { session, user: fullUser });\n\n return {\n session: { ...session },\n user: { ...fullUser },\n };\n}\n\n/**\n * Finalizes login after a challenge\n */\nexport async function finalizeLogin(userId: string, flags: SessionFlags) {\n const sessionToken = await generateSessionToken();\n const session = await createSession(sessionToken, userId, flags);\n await setSessionTokenCookie(sessionToken, session.expiresAt);\n\n const user = await getUserById(userId);\n\n if (user) {\n await eventBus.publish(\"auth:session-created\", { session, user });\n }\n\n return {\n session: session ? { ...session } : null,\n user: user ? { ...user } : null,\n };\n}\n\n/**\n * Sign Out\n */\nexport async function signOut() {\n const { session, user } = await getCurrentSession();\n if (session) {\n if (user) {\n await eventBus.publish(\"auth:signed-out\", { userId: user.id });\n }\n await invalidateSession(session.id);\n await deleteSessionTokenCookie();\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"logic.mjs","names":[],"sources":["../../../src/core/auth/logic.ts"],"sourcesContent":["\"use server\";\n\nimport { eq, inArray } from \"drizzle-orm\";\nimport {\n verifyPasswordHash,\n verifyPasswordStrength,\n} from \"../../server/auth/password\";\nimport {\n createUser,\n getUserById,\n getUserFromEmail,\n getUserPasswordHash,\n verifyUsernameInput,\n} from \"../../server/auth/user\";\nimport { db } from \"../../server/database/inject\";\nimport {\n permissionsTable,\n rolesTable,\n rolesToPermissionsTable,\n usersToPermissionsTable,\n usersToRolesTable,\n} from \"../../server/database/schema\";\nimport { eventBus } from \"../event-bus\";\nimport {\n augmentSession,\n augmentUser,\n registerIdentityAugmenter,\n registerPasswordResetSessionAugmenter,\n registerSessionAugmenter,\n} from \"./augment\";\nimport {\n createEmailVerificationRequest,\n sendVerificationEmail,\n setEmailVerificationRequestCookie,\n} from \"./email-verification\";\nimport {\n createSession,\n deleteSessionTokenCookie,\n generateSessionToken,\n getCurrentSession,\n invalidateSession,\n setSessionTokenCookie,\n} from \"./session\";\nimport type {\n AuthResponse,\n FullUser,\n Session,\n SessionFlags,\n User,\n UserPermission,\n UserRole,\n} from \"./types\";\nimport {\n type LoginInput,\n loginSchema,\n type RegisterInput,\n registerSchema,\n} from \"./validation\";\n\n/**\n * Podstawowy moduł rozszerzający tożsamość dla ról i uprawnień\n */\nasync function coreRbacAugmenter(user: User): Promise<Record<string, any>> {\n try {\n // 1. Fetch direct roles\n const userRoles = await db\n .select({ name: rolesTable.name })\n .from(usersToRolesTable)\n .innerJoin(rolesTable, eq(usersToRolesTable.roleId, rolesTable.id))\n .where(eq(usersToRolesTable.userId, user.id));\n\n const roles = userRoles.map((r) => r.name);\n\n // 2. Fetch direct permissions\n const userDirectPerms = await db\n .select({ name: permissionsTable.name })\n .from(usersToPermissionsTable)\n .innerJoin(\n permissionsTable,\n eq(usersToPermissionsTable.permissionId, permissionsTable.id),\n )\n .where(eq(usersToPermissionsTable.userId, user.id));\n\n const directPerms = userDirectPerms.map((p) => p.name);\n\n // 3. Fetch permissions from roles\n let rolePerms: string[] = [];\n if (roles.length > 0) {\n const roleIdsResult = await db\n .select({ id: rolesTable.id })\n .from(rolesTable)\n .where(inArray(rolesTable.name, roles));\n\n const roleIds = roleIdsResult.map((r) => r.id);\n\n if (roleIds.length > 0) {\n const rolePermsData = await db\n .select({ name: permissionsTable.name })\n .from(rolesToPermissionsTable)\n .innerJoin(\n permissionsTable,\n eq(rolesToPermissionsTable.permissionId, permissionsTable.id),\n )\n .where(inArray(rolesToPermissionsTable.roleId, roleIds));\n rolePerms = rolePermsData.map((p) => p.name);\n }\n }\n\n return {\n roles,\n permissions: Array.from(new Set([...directPerms, ...rolePerms])),\n };\n } catch (error) {\n console.error(\"[Auth:RBAC] Failed to augment user:\", error);\n return { roles: [], permissions: [] };\n }\n}\n\n/**\n * Registry for login validators (e.g. 2FA module)\n */\ntype AuthValidator = (userId: string) => Promise<AuthResponse | null>;\n\n/**\n * Registry for Security Requirements (e.g. checking if 2FA is needed for a session)\n */\ntype SecurityRequirement = (\n session: Session,\n user: FullUser,\n) => Promise<{ satisfied: boolean; redirect?: string } | null>;\n\n/**\n * Registry for password reset validators (e.g. 2FA module requiring check during reset)\n */\ntype PasswordResetValidator = (userId: string) => Promise<AuthResponse | null>;\n\n/**\n * Registry for email verification validators\n */\ntype EmailVerificationValidator = (\n userId: string,\n) => Promise<AuthResponse | null>;\n\nconst globalForAuth = globalThis as unknown as {\n __KRYO_AUTH_VALIDATORS__: Set<AuthValidator> | undefined;\n __KRYO_SECURITY_REQUIREMENTS__: Set<SecurityRequirement> | undefined;\n __KRYO_PASSWORD_RESET_VALIDATORS__: Set<PasswordResetValidator> | undefined;\n __KRYO_EMAIL_VERIFICATION_VALIDATORS__:\n | Set<EmailVerificationValidator>\n | undefined;\n};\n\nconst authValidators =\n globalForAuth.__KRYO_AUTH_VALIDATORS__ ?? new Set<AuthValidator>();\nconst securityRequirements =\n globalForAuth.__KRYO_SECURITY_REQUIREMENTS__ ??\n new Set<SecurityRequirement>();\nconst passwordResetValidators =\n globalForAuth.__KRYO_PASSWORD_RESET_VALIDATORS__ ??\n new Set<PasswordResetValidator>();\nconst emailVerificationValidators =\n globalForAuth.__KRYO_EMAIL_VERIFICATION_VALIDATORS__ ??\n new Set<EmailVerificationValidator>();\n\nglobalForAuth.__KRYO_AUTH_VALIDATORS__ = authValidators;\nglobalForAuth.__KRYO_SECURITY_REQUIREMENTS__ = securityRequirements;\nglobalForAuth.__KRYO_PASSWORD_RESET_VALIDATORS__ = passwordResetValidators;\nglobalForAuth.__KRYO_EMAIL_VERIFICATION_VALIDATORS__ =\n emailVerificationValidators;\n\nexport async function registerAuthValidator(validator: AuthValidator) {\n authValidators.add(validator);\n}\n\nexport async function registerPasswordResetValidator(\n validator: PasswordResetValidator,\n) {\n passwordResetValidators.add(validator);\n}\n\nexport async function registerEmailVerificationValidator(\n validator: EmailVerificationValidator,\n) {\n emailVerificationValidators.add(validator);\n}\n\nexport {\n registerIdentityAugmenter,\n registerSessionAugmenter,\n registerPasswordResetSessionAugmenter,\n augmentUser,\n augmentSession,\n};\n\nexport async function registerSecurityRequirement(\n requirement: SecurityRequirement,\n) {\n securityRequirements.add(requirement);\n}\n\nexport async function runPasswordResetValidators(\n userId: string,\n): Promise<AuthResponse | null> {\n for (const validator of passwordResetValidators) {\n const interception = await validator(userId);\n if (interception) return interception;\n }\n return null;\n}\n\nexport async function runEmailVerificationValidators(\n userId: string,\n): Promise<AuthResponse | null> {\n for (const validator of emailVerificationValidators) {\n const interception = await validator(userId);\n if (interception) return interception;\n }\n return null;\n}\n\n/**\n * Augments a base user with data from all registered modules.\n * This is now just a wrapper that includes core RBAC data.\n */\nexport async function performFullUserAugmentation(\n user: User,\n): Promise<FullUser> {\n const coreRbacData = await coreRbacAugmenter(user);\n return await augmentUser(user, coreRbacData);\n}\n\n/**\n * Checks if the current session satisfies all registered security requirements.\n */\nexport async function checkSecurity(\n session: Session,\n user: FullUser,\n requiredRoles?: UserRole[],\n requiredPermissions?: UserPermission[],\n fallbackRedirect?: string,\n) {\n if (!user) {\n console.warn(\"User is required for security check\");\n return { satisfied: false, redirect: fallbackRedirect ?? \"/signin\" };\n }\n\n const userRoles = Array.isArray(user.roles) ? user.roles : [];\n const userPermissions = Array.isArray(user.permissions)\n ? user.permissions\n : [];\n\n // 1. Core Role Check (At least one role must match)\n if (requiredRoles && requiredRoles.length > 0) {\n const hasRole = requiredRoles.some((role) => userRoles.includes(role));\n if (!hasRole) {\n console.warn(`User lacks required roles: ${requiredRoles.join(\", \")}`);\n return {\n satisfied: false,\n redirect: fallbackRedirect,\n };\n }\n }\n\n // 2. Core Permission Check (ALL permissions must match)\n if (requiredPermissions && requiredPermissions.length > 0) {\n const hasAllPermissions = requiredPermissions.every((perm) =>\n userPermissions.includes(perm),\n );\n if (!hasAllPermissions) {\n console.warn(\n `User lacks required permissions: ${requiredPermissions.join(\", \")}`,\n );\n\n return {\n satisfied: false,\n redirect: fallbackRedirect,\n };\n }\n }\n\n // 3. Modular Requirements Check\n if (securityRequirements) {\n for (const requirement of securityRequirements) {\n try {\n const result = await requirement(session, user);\n if (result && !result.satisfied) {\n return {\n ...result,\n redirect: result.redirect ?? fallbackRedirect,\n };\n }\n } catch (error) {\n console.error(\"[Auth:Security] Requirement failed:\", error);\n }\n }\n }\n return { satisfied: true };\n}\n\n/**\n * Sign In Logic\n */\nexport async function signIn(data: LoginInput): Promise<AuthResponse> {\n const { email, password } = await loginSchema.parseAsync(data);\n\n const user = await getUserFromEmail(email);\n if (!user) {\n return { status: \"ERROR\", message: \"Invalid email or password\" };\n }\n\n const passwordHash = await getUserPasswordHash(user.id);\n if (!passwordHash || !(await verifyPasswordHash(passwordHash, password))) {\n return { status: \"ERROR\", message: \"Invalid email or password\" };\n }\n\n // Interception Layer\n for (const validator of authValidators) {\n const interception = await validator(user.id);\n if (interception) return interception;\n }\n\n const sessionFlags: SessionFlags = {};\n const sessionToken = await generateSessionToken();\n const session = await createSession(sessionToken, user.id, sessionFlags);\n await setSessionTokenCookie(sessionToken, session.expiresAt);\n\n const fullUser = await performFullUserAugmentation(user);\n await eventBus.publish(\"auth:session-created\", { session, user: fullUser });\n\n return {\n status: \"SUCCESS\",\n session: { ...session },\n user: { ...fullUser },\n };\n}\n\n/**\n * Sign Up Logic\n */\nexport async function signUp(data: RegisterInput) {\n const { email, username, password } = registerSchema.parse(data);\n\n if (!(await verifyUsernameInput(username))) {\n throw new Error(\"Invalid username\");\n }\n\n if (!(await verifyPasswordStrength(password))) {\n throw new Error(\"Weak password\");\n }\n\n const user = await createUser(email, username, password);\n const verificationRequest = await createEmailVerificationRequest(\n user.id,\n user.email,\n );\n\n await sendVerificationEmail(\n verificationRequest.email,\n verificationRequest.code,\n );\n await setEmailVerificationRequestCookie(verificationRequest);\n\n const sessionFlags: SessionFlags = {};\n const sessionToken = await generateSessionToken();\n const session = await createSession(sessionToken, user.id, sessionFlags);\n await setSessionTokenCookie(sessionToken, session.expiresAt);\n\n const fullUser = await performFullUserAugmentation(user);\n await eventBus.publish(\"auth:session-created\", { session, user: fullUser });\n\n return {\n session: { ...session },\n user: { ...fullUser },\n };\n}\n\n/**\n * Finalizes login after a challenge\n */\nexport async function finalizeLogin(userId: string, flags: SessionFlags) {\n const sessionToken = await generateSessionToken();\n const session = await createSession(sessionToken, userId, flags);\n await setSessionTokenCookie(sessionToken, session.expiresAt);\n\n const user = await getUserById(userId);\n\n if (user) {\n await eventBus.publish(\"auth:session-created\", { session, user });\n }\n\n return {\n session: session ? { ...session } : null,\n user: user ? { ...user } : null,\n };\n}\n\n/**\n * Sign Out\n */\nexport async function signOut() {\n const { session, user } = await getCurrentSession();\n if (session) {\n if (user) {\n await eventBus.publish(\"auth:signed-out\", { userId: user.id });\n }\n await invalidateSession(session.id);\n await deleteSessionTokenCookie();\n }\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"password-reset.mjs","names":[],"sources":["../../../src/core/auth/password-reset.ts"],"sourcesContent":["\"use server\";\n\nimport { sha256 } from \"@oslojs/crypto/sha2\";\nimport { encodeHexLowerCase } from \"@oslojs/encoding\";\nimport { addHours } from \"date-fns\";\nimport { eq } from \"drizzle-orm\";\nimport { cookies } from \"next/headers\";\nimport { db } from \"../../server/database/inject.js\";\nimport {\n passwordResetSessionTable,\n userTable,\n} from \"../../server/database/schema.js\";\nimport { sendResetPassword } from \"../../server/emails/index.js\";\nimport { augmentPasswordResetSession } from \"./augment.js\";\nimport { performFullUserAugmentation } from \"./logic.js\";\nimport type { PasswordResetAuthSession, PasswordResetSession } from \"./types.js\";\nimport { generateRandomOTP } from \"./utils/encode.js\";\n\n/**\n * Creates a new password reset session.\n */\nexport async function createPasswordResetSession(\n token: string,\n userId: string,\n email: string,\n): Promise<PasswordResetSession> {\n const sessionId = encodeHexLowerCase(sha256(new TextEncoder().encode(token)));\n\n const [session] = await db\n .insert(passwordResetSessionTable)\n .values({\n id: sessionId,\n email: email,\n code: generateRandomOTP(),\n expiresAt: new Date(addHours(new Date(), 1)),\n userId: userId,\n })\n .returning();\n\n return session;\n}\n\n/**\n * Validates the password reset session token and retrieves user data.\n * The user data is augmented by registered modules (e.g. 2FA).\n */\nexport async function validatePasswordResetSessionToken(\n token: string,\n): Promise<PasswordResetAuthSession> {\n const sessionId = encodeHexLowerCase(sha256(new TextEncoder().encode(token)));\n\n const [row] = await db\n .select({\n session: passwordResetSessionTable,\n user: userTable,\n })\n .from(passwordResetSessionTable)\n .innerJoin(userTable, eq(passwordResetSessionTable.userId, userTable.id))\n .where(eq(passwordResetSessionTable.id, sessionId));\n\n if (!row || !row.user) {\n return { session: null, user: null };\n }\n\n const { session: baseSession, user: baseUser } = row;\n\n // Check for expiration\n if (new Date() > baseSession.expiresAt) {\n await db\n .delete(passwordResetSessionTable)\n .where(eq(passwordResetSessionTable.id, baseSession.id));\n return { session: null, user: null };\n }\n\n // STRICTLY remove non-serializable and sensitive fields\n const { password, recovery_code, ...safeUser } = baseUser;\n\n // AUGMENT (EXTENSIBILITY POINTS)\n const user = await performFullUserAugmentation(safeUser as any);\n const session = await augmentPasswordResetSession(\n baseSession as PasswordResetSession,\n );\n\n return { session, user };\n}\n\n/**\n * Marks the password reset session as email verified.\n */\nexport async function setPasswordResetSessionAsEmailVerified(\n sessionId: string,\n): Promise<void> {\n await db\n .update(passwordResetSessionTable)\n .set({\n emailVerified: true,\n })\n .where(eq(passwordResetSessionTable.id, sessionId));\n}\n\n/**\n * Invalidates all password reset sessions for a user.\n */\nexport async function invalidateUserPasswordResetSessions(\n userId: string,\n): Promise<void> {\n await db\n .delete(passwordResetSessionTable)\n .where(eq(passwordResetSessionTable.userId, userId));\n}\n\n/**\n * Validates the current password reset session from cookies.\n */\nexport async function getCurrentPasswordResetSession(): Promise<PasswordResetAuthSession> {\n const cookieStore = await cookies();\n const token = cookieStore.get(\"password_reset_session\")?.value ?? null;\n\n if (token === null) {\n return { session: null, user: null };\n }\n\n const result = await validatePasswordResetSessionToken(token);\n\n if (result.session === null) {\n await deletePasswordResetSessionTokenCookie();\n }\n\n return result;\n}\n\n/**\n * Sets the password reset session token cookie.\n */\nexport async function setPasswordResetSessionTokenCookie(\n token: string,\n expiresAt: Date,\n): Promise<void> {\n const cookieStore = await cookies();\n\n cookieStore.set(\"password_reset_session\", token, {\n expires: expiresAt,\n sameSite: \"lax\",\n httpOnly: true,\n path: \"/\",\n secure: process.env.NODE_ENV === \"production\",\n });\n}\n\n/**\n * Deletes the password reset session token cookie.\n */\nexport async function deletePasswordResetSessionTokenCookie(): Promise<void> {\n const cookieStore = await cookies();\n cookieStore.delete(\"password_reset_session\");\n}\n\n/**\n * Sends a password reset email with the OTP code.\n */\nexport async function sendPasswordResetEmail(\n email: string,\n code: string,\n): Promise<void> {\n await sendResetPassword(email, code);\n}\n"],"mappings":"6lBAqBA,eAAsB,EACpB,EACA,EACA,EAC+B,CAC/B,IAAM,EAAY,EAAmB,EAAO,IAAI,aAAa,CAAC,OAAO,EAAM,CAAC,CAAC,CAEvE,CAAC,GAAW,MAAM,EACrB,OAAO,EAA0B,CACjC,OAAO,CACN,GAAI,EACG,QACP,KAAM,GAAmB,CACzB,UAAW,IAAI,KAAK,EAAS,IAAI,KAAQ,EAAE,CAAC,CACpC,SACT,CAAC,CACD,WAAW,CAEd,OAAO,EAOT,eAAsB,EACpB,EACmC,CACnC,IAAM,EAAY,EAAmB,EAAO,IAAI,aAAa,CAAC,OAAO,EAAM,CAAC,CAAC,CAEvE,CAAC,GAAO,MAAM,EACjB,OAAO,CACN,QAAS,EACT,KAAM,EACP,CAAC,CACD,KAAK,EAA0B,CAC/B,UAAU,EAAW,EAAG,EAA0B,OAAQ,EAAU,GAAG,CAAC,CACxE,MAAM,EAAG,EAA0B,GAAI,EAAU,CAAC,CAErD,GAAI,CAAC,GAAO,CAAC,EAAI,KACf,MAAO,CAAE,QAAS,KAAM,KAAM,KAAM,CAGtC,GAAM,CAAE,QAAS,EAAa,KAAM,GAAa,EAGjD,GAAI,IAAI,KAAS,EAAY,UAI3B,OAHA,MAAM,EACH,OAAO,EAA0B,CACjC,MAAM,EAAG,EAA0B,GAAI,EAAY,GAAG,CAAC,CACnD,CAAE,QAAS,KAAM,KAAM,KAAM,CAItC,GAAM,CAAE,WAAU,gBAAe,GAAG,GAAa,EAG3C,EAAO,MAAM,EAA4B,EAAgB,CAK/D,MAAO,CAAE,QAJO,MAAM,EACpB,EACD,CAEiB,OAAM,CAM1B,eAAsB,EACpB,EACe,CACf,MAAM,EACH,OAAO,EAA0B,CACjC,IAAI,CACH,cAAe,GAChB,CAAC,CACD,MAAM,EAAG,EAA0B,GAAI,EAAU,CAAC,CAMvD,eAAsB,EACpB,EACe,CACf,MAAM,EACH,OAAO,EAA0B,CACjC,MAAM,EAAG,EAA0B,OAAQ,EAAO,CAAC,CAMxD,eAAsB,GAAoE,CAExF,IAAM,GADc,MAAM,GAAS,EACT,IAAI,yBAAyB,EAAE,OAAS,KAElE,GAAI,IAAU,KACZ,MAAO,CAAE,QAAS,KAAM,KAAM,KAAM,CAGtC,IAAM,EAAS,MAAM,EAAkC,EAAM,CAM7D,OAJI,EAAO,UAAY,MACrB,MAAM,GAAuC,CAGxC,EAMT,eAAsB,EACpB,EACA,EACe,EACK,MAAM,GAAS,EAEvB,IAAI,yBAA0B,EAAO,CAC/C,QAAS,EACT,SAAU,MACV,SAAU,GACV,KAAM,IACN,OAAQ,QAAQ,IAAI,WAAa,aAClC,CAAC,CAMJ,eAAsB,GAAuD,EACvD,MAAM,GAAS,EACvB,OAAO,yBAAyB,CAM9C,eAAsB,EACpB,EACA,EACe,CACf,MAAM,EAAkB,EAAO,EAAK"}
1
+ {"version":3,"file":"password-reset.mjs","names":[],"sources":["../../../src/core/auth/password-reset.ts"],"sourcesContent":["\"use server\";\n\nimport { sha256 } from \"@oslojs/crypto/sha2\";\nimport { encodeHexLowerCase } from \"@oslojs/encoding\";\nimport { addHours } from \"date-fns\";\nimport { eq } from \"drizzle-orm\";\nimport { cookies } from \"next/headers\";\nimport { db } from \"../../server/database/inject\";\nimport {\n passwordResetSessionTable,\n userTable,\n} from \"../../server/database/schema\";\nimport { sendResetPassword } from \"../../server/emails/index\";\nimport { augmentPasswordResetSession } from \"./augment\";\nimport { performFullUserAugmentation } from \"./logic\";\nimport type { PasswordResetAuthSession, PasswordResetSession } from \"./types\";\nimport { generateRandomOTP } from \"./utils/encode\";\n\n/**\n * Creates a new password reset session.\n */\nexport async function createPasswordResetSession(\n token: string,\n userId: string,\n email: string,\n): Promise<PasswordResetSession> {\n const sessionId = encodeHexLowerCase(sha256(new TextEncoder().encode(token)));\n\n const [session] = await db\n .insert(passwordResetSessionTable)\n .values({\n id: sessionId,\n email: email,\n code: generateRandomOTP(),\n expiresAt: new Date(addHours(new Date(), 1)),\n userId: userId,\n })\n .returning();\n\n return session;\n}\n\n/**\n * Validates the password reset session token and retrieves user data.\n * The user data is augmented by registered modules (e.g. 2FA).\n */\nexport async function validatePasswordResetSessionToken(\n token: string,\n): Promise<PasswordResetAuthSession> {\n const sessionId = encodeHexLowerCase(sha256(new TextEncoder().encode(token)));\n\n const [row] = await db\n .select({\n session: passwordResetSessionTable,\n user: userTable,\n })\n .from(passwordResetSessionTable)\n .innerJoin(userTable, eq(passwordResetSessionTable.userId, userTable.id))\n .where(eq(passwordResetSessionTable.id, sessionId));\n\n if (!row || !row.user) {\n return { session: null, user: null };\n }\n\n const { session: baseSession, user: baseUser } = row;\n\n // Check for expiration\n if (new Date() > baseSession.expiresAt) {\n await db\n .delete(passwordResetSessionTable)\n .where(eq(passwordResetSessionTable.id, baseSession.id));\n return { session: null, user: null };\n }\n\n // STRICTLY remove non-serializable and sensitive fields\n const { password, recovery_code, ...safeUser } = baseUser;\n\n // AUGMENT (EXTENSIBILITY POINTS)\n const user = await performFullUserAugmentation(safeUser as any);\n const session = await augmentPasswordResetSession(\n baseSession as PasswordResetSession,\n );\n\n return { session, user };\n}\n\n/**\n * Marks the password reset session as email verified.\n */\nexport async function setPasswordResetSessionAsEmailVerified(\n sessionId: string,\n): Promise<void> {\n await db\n .update(passwordResetSessionTable)\n .set({\n emailVerified: true,\n })\n .where(eq(passwordResetSessionTable.id, sessionId));\n}\n\n/**\n * Invalidates all password reset sessions for a user.\n */\nexport async function invalidateUserPasswordResetSessions(\n userId: string,\n): Promise<void> {\n await db\n .delete(passwordResetSessionTable)\n .where(eq(passwordResetSessionTable.userId, userId));\n}\n\n/**\n * Validates the current password reset session from cookies.\n */\nexport async function getCurrentPasswordResetSession(): Promise<PasswordResetAuthSession> {\n const cookieStore = await cookies();\n const token = cookieStore.get(\"password_reset_session\")?.value ?? null;\n\n if (token === null) {\n return { session: null, user: null };\n }\n\n const result = await validatePasswordResetSessionToken(token);\n\n if (result.session === null) {\n await deletePasswordResetSessionTokenCookie();\n }\n\n return result;\n}\n\n/**\n * Sets the password reset session token cookie.\n */\nexport async function setPasswordResetSessionTokenCookie(\n token: string,\n expiresAt: Date,\n): Promise<void> {\n const cookieStore = await cookies();\n\n cookieStore.set(\"password_reset_session\", token, {\n expires: expiresAt,\n sameSite: \"lax\",\n httpOnly: true,\n path: \"/\",\n secure: process.env.NODE_ENV === \"production\",\n });\n}\n\n/**\n * Deletes the password reset session token cookie.\n */\nexport async function deletePasswordResetSessionTokenCookie(): Promise<void> {\n const cookieStore = await cookies();\n cookieStore.delete(\"password_reset_session\");\n}\n\n/**\n * Sends a password reset email with the OTP code.\n */\nexport async function sendPasswordResetEmail(\n email: string,\n code: string,\n): Promise<void> {\n await sendResetPassword(email, code);\n}\n"],"mappings":"6lBAqBA,eAAsB,EACpB,EACA,EACA,EAC+B,CAC/B,IAAM,EAAY,EAAmB,EAAO,IAAI,aAAa,CAAC,OAAO,EAAM,CAAC,CAAC,CAEvE,CAAC,GAAW,MAAM,EACrB,OAAO,EAA0B,CACjC,OAAO,CACN,GAAI,EACG,QACP,KAAM,GAAmB,CACzB,UAAW,IAAI,KAAK,EAAS,IAAI,KAAQ,EAAE,CAAC,CACpC,SACT,CAAC,CACD,WAAW,CAEd,OAAO,EAOT,eAAsB,EACpB,EACmC,CACnC,IAAM,EAAY,EAAmB,EAAO,IAAI,aAAa,CAAC,OAAO,EAAM,CAAC,CAAC,CAEvE,CAAC,GAAO,MAAM,EACjB,OAAO,CACN,QAAS,EACT,KAAM,EACP,CAAC,CACD,KAAK,EAA0B,CAC/B,UAAU,EAAW,EAAG,EAA0B,OAAQ,EAAU,GAAG,CAAC,CACxE,MAAM,EAAG,EAA0B,GAAI,EAAU,CAAC,CAErD,GAAI,CAAC,GAAO,CAAC,EAAI,KACf,MAAO,CAAE,QAAS,KAAM,KAAM,KAAM,CAGtC,GAAM,CAAE,QAAS,EAAa,KAAM,GAAa,EAGjD,GAAI,IAAI,KAAS,EAAY,UAI3B,OAHA,MAAM,EACH,OAAO,EAA0B,CACjC,MAAM,EAAG,EAA0B,GAAI,EAAY,GAAG,CAAC,CACnD,CAAE,QAAS,KAAM,KAAM,KAAM,CAItC,GAAM,CAAE,WAAU,gBAAe,GAAG,GAAa,EAG3C,EAAO,MAAM,EAA4B,EAAgB,CAK/D,MAAO,CAAE,QAJO,MAAM,EACpB,EACD,CAEiB,OAAM,CAM1B,eAAsB,EACpB,EACe,CACf,MAAM,EACH,OAAO,EAA0B,CACjC,IAAI,CACH,cAAe,GAChB,CAAC,CACD,MAAM,EAAG,EAA0B,GAAI,EAAU,CAAC,CAMvD,eAAsB,EACpB,EACe,CACf,MAAM,EACH,OAAO,EAA0B,CACjC,MAAM,EAAG,EAA0B,OAAQ,EAAO,CAAC,CAMxD,eAAsB,GAAoE,CAExF,IAAM,GADc,MAAM,GAAS,EACT,IAAI,yBAAyB,EAAE,OAAS,KAElE,GAAI,IAAU,KACZ,MAAO,CAAE,QAAS,KAAM,KAAM,KAAM,CAGtC,IAAM,EAAS,MAAM,EAAkC,EAAM,CAM7D,OAJI,EAAO,UAAY,MACrB,MAAM,GAAuC,CAGxC,EAMT,eAAsB,EACpB,EACA,EACe,EACK,MAAM,GAAS,EAEvB,IAAI,yBAA0B,EAAO,CAC/C,QAAS,EACT,SAAU,MACV,SAAU,GACV,KAAM,IACN,OAAQ,QAAQ,IAAI,WAAa,aAClC,CAAC,CAMJ,eAAsB,GAAuD,EACvD,MAAM,GAAS,EACvB,OAAO,yBAAyB,CAM9C,eAAsB,EACpB,EACA,EACe,CACf,MAAM,EAAkB,EAAO,EAAK"}
@@ -16,8 +16,8 @@ declare function getRoleById(roleId: string): Promise<{
16
16
  description: string | null;
17
17
  }>;
18
18
  declare function createRole(name: string, description?: string): Promise<{
19
- name: string;
20
19
  id: string;
20
+ name: string;
21
21
  description: string | null;
22
22
  }[]>;
23
23
  declare function deleteRole(roleId: string): Promise<pg.QueryResult<never>>;
@@ -27,8 +27,8 @@ declare function getPermissions(): Promise<{
27
27
  description: string | null;
28
28
  }[]>;
29
29
  declare function createPermission(name: string, description?: string): Promise<{
30
- name: string;
31
30
  id: string;
31
+ name: string;
32
32
  description: string | null;
33
33
  }[]>;
34
34
  declare function deletePermission(permissionId: string): Promise<pg.QueryResult<never>>;
@@ -16,8 +16,8 @@ declare function getRoleById(roleId: string): Promise<{
16
16
  description: string | null;
17
17
  }>;
18
18
  declare function createRole(name: string, description?: string): Promise<{
19
- name: string;
20
19
  id: string;
20
+ name: string;
21
21
  description: string | null;
22
22
  }[]>;
23
23
  declare function deleteRole(roleId: string): Promise<pg.QueryResult<never>>;
@@ -27,8 +27,8 @@ declare function getPermissions(): Promise<{
27
27
  description: string | null;
28
28
  }[]>;
29
29
  declare function createPermission(name: string, description?: string): Promise<{
30
- name: string;
31
30
  id: string;
31
+ name: string;
32
32
  description: string | null;
33
33
  }[]>;
34
34
  declare function deletePermission(permissionId: string): Promise<pg.QueryResult<never>>;
@@ -1 +1 @@
1
- {"version":3,"file":"rbac.mjs","names":[],"sources":["../../../src/core/auth/rbac.ts"],"sourcesContent":["\"use server\";\n\nimport { and, eq, inArray } from \"drizzle-orm\";\nimport { db } from \"../../server/database/inject.js\";\nimport {\n permissionsTable,\n rolesTable,\n rolesToPermissionsTable,\n usersToPermissionsTable,\n usersToRolesTable,\n} from \"../../server/database/schema.js\";\nimport { notificationService } from \"../notifications/index.js\";\n\n// Ensure notification service is loaded\nif (typeof window === \"undefined\") {\n notificationService.init();\n}\n\n/**\n * CORE RBAC LOGIC\n * This file handles all database operations for Roles and Permissions.\n */\n\n// --- Roles ---\n\nexport async function getRoles() {\n return await db.select().from(rolesTable).orderBy(rolesTable.name);\n}\n\nexport async function getRoleById(roleId: string) {\n const [role] = await db\n .select()\n .from(rolesTable)\n .where(eq(rolesTable.id, roleId));\n return role;\n}\n\nexport async function createRole(name: string, description?: string) {\n return await db.insert(rolesTable).values({ name, description }).returning();\n}\n\nexport async function deleteRole(roleId: string) {\n return await db.delete(rolesTable).where(eq(rolesTable.id, roleId));\n}\n\n// --- Permissions ---\n\nexport async function getPermissions() {\n return await db\n .select()\n .from(permissionsTable)\n .orderBy(permissionsTable.name);\n}\n\nexport async function createPermission(name: string, description?: string) {\n return await db\n .insert(permissionsTable)\n .values({ name, description })\n .returning();\n}\n\nexport async function deletePermission(permissionId: string) {\n return await db\n .delete(permissionsTable)\n .where(eq(permissionsTable.id, permissionId));\n}\n\n// --- Mappings ---\n\nexport async function getRolePermissions(roleId: string) {\n return await db\n .select({\n id: permissionsTable.id,\n name: permissionsTable.name,\n })\n .from(rolesToPermissionsTable)\n .innerJoin(\n permissionsTable,\n eq(rolesToPermissionsTable.permissionId, permissionsTable.id),\n )\n .where(eq(rolesToPermissionsTable.roleId, roleId));\n}\n\nexport async function assignPermissionToRole(\n roleId: string,\n permissionId: string,\n) {\n return await db\n .insert(rolesToPermissionsTable)\n .values({ roleId, permissionId })\n .onConflictDoNothing();\n}\n\nexport async function revokePermissionFromRole(\n roleId: string,\n permissionId: string,\n) {\n return await db\n .delete(rolesToPermissionsTable)\n .where(\n and(\n eq(rolesToPermissionsTable.roleId, roleId),\n eq(rolesToPermissionsTable.permissionId, permissionId),\n ),\n );\n}\n\n// --- User Assignment ---\n\nexport async function assignRoleToUser(userId: string, roleId: string) {\n return await db\n .insert(usersToRolesTable)\n .values({ userId, roleId })\n .onConflictDoNothing();\n}\n\nexport async function revokeRoleFromUser(userId: string, roleId: string) {\n return await db\n .delete(usersToRolesTable)\n .where(\n and(\n eq(usersToRolesTable.userId, userId),\n eq(usersToRolesTable.roleId, roleId),\n ),\n );\n}\n\nexport async function assignPermissionToUser(\n userId: string,\n permissionId: string,\n) {\n return await db\n .insert(usersToPermissionsTable)\n .values({ userId, permissionId })\n .onConflictDoNothing();\n}\n\nexport async function revokePermissionFromUser(\n userId: string,\n permissionId: string,\n) {\n return await db\n .delete(usersToPermissionsTable)\n .where(\n and(\n eq(usersToPermissionsTable.userId, userId),\n eq(usersToPermissionsTable.permissionId, permissionId),\n ),\n );\n}\n\nexport async function getUserRbacData(userId: string) {\n const roles = await db\n .select({\n id: rolesTable.id,\n name: rolesTable.name,\n })\n .from(usersToRolesTable)\n .innerJoin(rolesTable, eq(usersToRolesTable.roleId, rolesTable.id))\n .where(eq(usersToRolesTable.userId, userId));\n\n const directPermissions = await db\n .select({\n id: permissionsTable.id,\n name: permissionsTable.name,\n })\n .from(usersToPermissionsTable)\n .innerJoin(\n permissionsTable,\n eq(usersToPermissionsTable.permissionId, permissionsTable.id),\n )\n .where(eq(usersToPermissionsTable.userId, userId));\n\n // Fetch inherited permissions from roles\n let rolePermissions: { id: string; name: string }[] = [];\n if (roles.length > 0) {\n const roleIds = roles.map((r) => r.id);\n rolePermissions = await db\n .select({\n id: permissionsTable.id,\n name: permissionsTable.name,\n })\n .from(rolesToPermissionsTable)\n .innerJoin(\n permissionsTable,\n eq(rolesToPermissionsTable.permissionId, permissionsTable.id),\n )\n .where(inArray(rolesToPermissionsTable.roleId, roleIds));\n }\n\n // Combine for effective permissions\n const effectiveMap = new Map<string, { id: string; name: string }>();\n for (const p of [...directPermissions, ...rolePermissions]) {\n effectiveMap.set(p.id, p);\n }\n\n return {\n roles,\n directPermissions,\n effectivePermissions: Array.from(effectiveMap.values()),\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"rbac.mjs","names":[],"sources":["../../../src/core/auth/rbac.ts"],"sourcesContent":["\"use server\";\n\nimport { and, eq, inArray } from \"drizzle-orm\";\nimport { db } from \"../../server/database/inject\";\nimport {\n permissionsTable,\n rolesTable,\n rolesToPermissionsTable,\n usersToPermissionsTable,\n usersToRolesTable,\n} from \"../../server/database/schema\";\nimport { notificationService } from \"../notifications/index\";\n\n// Ensure notification service is loaded\nif (typeof window === \"undefined\") {\n notificationService.init();\n}\n\n/**\n * CORE RBAC LOGIC\n * This file handles all database operations for Roles and Permissions.\n */\n\n// --- Roles ---\n\nexport async function getRoles() {\n return await db.select().from(rolesTable).orderBy(rolesTable.name);\n}\n\nexport async function getRoleById(roleId: string) {\n const [role] = await db\n .select()\n .from(rolesTable)\n .where(eq(rolesTable.id, roleId));\n return role;\n}\n\nexport async function createRole(name: string, description?: string) {\n return await db.insert(rolesTable).values({ name, description }).returning();\n}\n\nexport async function deleteRole(roleId: string) {\n return await db.delete(rolesTable).where(eq(rolesTable.id, roleId));\n}\n\n// --- Permissions ---\n\nexport async function getPermissions() {\n return await db\n .select()\n .from(permissionsTable)\n .orderBy(permissionsTable.name);\n}\n\nexport async function createPermission(name: string, description?: string) {\n return await db\n .insert(permissionsTable)\n .values({ name, description })\n .returning();\n}\n\nexport async function deletePermission(permissionId: string) {\n return await db\n .delete(permissionsTable)\n .where(eq(permissionsTable.id, permissionId));\n}\n\n// --- Mappings ---\n\nexport async function getRolePermissions(roleId: string) {\n return await db\n .select({\n id: permissionsTable.id,\n name: permissionsTable.name,\n })\n .from(rolesToPermissionsTable)\n .innerJoin(\n permissionsTable,\n eq(rolesToPermissionsTable.permissionId, permissionsTable.id),\n )\n .where(eq(rolesToPermissionsTable.roleId, roleId));\n}\n\nexport async function assignPermissionToRole(\n roleId: string,\n permissionId: string,\n) {\n return await db\n .insert(rolesToPermissionsTable)\n .values({ roleId, permissionId })\n .onConflictDoNothing();\n}\n\nexport async function revokePermissionFromRole(\n roleId: string,\n permissionId: string,\n) {\n return await db\n .delete(rolesToPermissionsTable)\n .where(\n and(\n eq(rolesToPermissionsTable.roleId, roleId),\n eq(rolesToPermissionsTable.permissionId, permissionId),\n ),\n );\n}\n\n// --- User Assignment ---\n\nexport async function assignRoleToUser(userId: string, roleId: string) {\n return await db\n .insert(usersToRolesTable)\n .values({ userId, roleId })\n .onConflictDoNothing();\n}\n\nexport async function revokeRoleFromUser(userId: string, roleId: string) {\n return await db\n .delete(usersToRolesTable)\n .where(\n and(\n eq(usersToRolesTable.userId, userId),\n eq(usersToRolesTable.roleId, roleId),\n ),\n );\n}\n\nexport async function assignPermissionToUser(\n userId: string,\n permissionId: string,\n) {\n return await db\n .insert(usersToPermissionsTable)\n .values({ userId, permissionId })\n .onConflictDoNothing();\n}\n\nexport async function revokePermissionFromUser(\n userId: string,\n permissionId: string,\n) {\n return await db\n .delete(usersToPermissionsTable)\n .where(\n and(\n eq(usersToPermissionsTable.userId, userId),\n eq(usersToPermissionsTable.permissionId, permissionId),\n ),\n );\n}\n\nexport async function getUserRbacData(userId: string) {\n const roles = await db\n .select({\n id: rolesTable.id,\n name: rolesTable.name,\n })\n .from(usersToRolesTable)\n .innerJoin(rolesTable, eq(usersToRolesTable.roleId, rolesTable.id))\n .where(eq(usersToRolesTable.userId, userId));\n\n const directPermissions = await db\n .select({\n id: permissionsTable.id,\n name: permissionsTable.name,\n })\n .from(usersToPermissionsTable)\n .innerJoin(\n permissionsTable,\n eq(usersToPermissionsTable.permissionId, permissionsTable.id),\n )\n .where(eq(usersToPermissionsTable.userId, userId));\n\n // Fetch inherited permissions from roles\n let rolePermissions: { id: string; name: string }[] = [];\n if (roles.length > 0) {\n const roleIds = roles.map((r) => r.id);\n rolePermissions = await db\n .select({\n id: permissionsTable.id,\n name: permissionsTable.name,\n })\n .from(rolesToPermissionsTable)\n .innerJoin(\n permissionsTable,\n eq(rolesToPermissionsTable.permissionId, permissionsTable.id),\n )\n .where(inArray(rolesToPermissionsTable.roleId, roleIds));\n }\n\n // Combine for effective permissions\n const effectiveMap = new Map<string, { id: string; name: string }>();\n for (const p of [...directPermissions, ...rolePermissions]) {\n effectiveMap.set(p.id, p);\n }\n\n return {\n roles,\n directPermissions,\n effectivePermissions: Array.from(effectiveMap.values()),\n };\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"session.mjs","names":[],"sources":["../../../src/core/auth/session.ts"],"sourcesContent":["\"use server\";\n\nimport { sha256 } from \"@oslojs/crypto/sha2\";\nimport {\n encodeBase32LowerCaseNoPadding,\n encodeHexLowerCase,\n} from \"@oslojs/encoding\";\nimport { addDays } from \"date-fns\";\nimport { and, eq, ne } from \"drizzle-orm\";\nimport { cookies, headers } from \"next/headers\";\nimport { redirect } from \"next/navigation\";\nimport { db } from \"../../server/database/inject.js\";\nimport { sessionTable, userTable } from \"../../server/database/schema.js\";\nimport { augmentSession } from \"./augment.js\";\nimport { performFullUserAugmentation } from \"./logic.js\";\n\nimport type {\n AuthSession,\n Session,\n SessionFlags,\n User,\n UserSession,\n} from \"./types.js\";\n\n/**\n * Returns the user's IP address.\n */\nexport async function getIPAddress(): Promise<string | null> {\n return (await headers()).get(\"x-forwarded-for\");\n}\n\n/**\n * Validates the session token.\n */\nexport async function validateSessionToken(\n token: string,\n): Promise<AuthSession> {\n const sessionId = encodeHexLowerCase(sha256(new TextEncoder().encode(token)));\n\n const [row] = await db\n .select({\n session: sessionTable,\n user: userTable,\n })\n .from(sessionTable)\n .innerJoin(userTable, eq(sessionTable.userId, userTable.id))\n .where(eq(sessionTable.id, sessionId));\n\n if (!row || !row.user) {\n return { session: null, user: null };\n }\n\n const { session: baseSession, user: baseUser } = row;\n\n // STRICTLY remove non-serializable and sensitive fields\n const { password, recovery_code, ...safeUser } = baseUser;\n\n // Check if session is expired\n if (new Date() > baseSession.expiresAt) {\n await db.delete(sessionTable).where(eq(sessionTable.id, baseSession.id));\n return { session: null, user: null };\n }\n\n // AUGMENT (EXTENSIBILITY POINTS)\n const augmentedUser = await performFullUserAugmentation(safeUser as User);\n const augmentedSession = await augmentSession(baseSession as Session);\n\n // ENSURE PLAIN OBJECTS for Client Components\n return {\n session: augmentedSession ? { ...augmentedSession } : null,\n user: augmentedUser ? { ...augmentedUser } : null,\n };\n}\n\n/**\n * Returns the current user session from cookies.\n */\nexport const getCurrentSession = async (): Promise<AuthSession> => {\n const cookieStore = await cookies();\n const token = cookieStore.get(\"session\")?.value ?? null;\n\n if (token === null) {\n return { session: null, user: null };\n }\n\n return await validateSessionToken(token);\n};\n\n/**\n * Invalidates a single session.\n */\nexport async function invalidateSession(sessionId: string): Promise<void> {\n await db.delete(sessionTable).where(eq(sessionTable.id, sessionId));\n}\n\n/**\n * Invalidates all user sessions.\n */\nexport async function invalidateUserSessions(userId: string): Promise<void> {\n await db.delete(sessionTable).where(eq(sessionTable.userId, userId));\n}\n\n/**\n * Sets the session token in a cookie.\n */\nexport async function setSessionTokenCookie(\n token: string,\n expiresAt: Date,\n): Promise<void> {\n const cookieStore = await cookies();\n cookieStore.set(\"session\", token, {\n httpOnly: true,\n path: \"/\",\n secure: process.env.NODE_ENV === \"production\",\n sameSite: \"lax\",\n expires: expiresAt,\n });\n}\n\n/**\n * Removes the session token cookie.\n */\nexport async function deleteSessionTokenCookie(): Promise<void> {\n const cookieStore = await cookies();\n cookieStore.delete(\"session\");\n}\n\n/**\n * Generates a new random session token.\n */\nexport async function generateSessionToken(): Promise<string> {\n const tokenBytes = new Uint8Array(20);\n crypto.getRandomValues(tokenBytes);\n return encodeBase32LowerCaseNoPadding(tokenBytes).toLowerCase();\n}\n\n/**\n * Creates a new session in the database.\n */\nexport async function createSession(\n token: string,\n userId: string,\n flags: SessionFlags,\n): Promise<Session> {\n const sessionId = encodeHexLowerCase(sha256(new TextEncoder().encode(token)));\n\n const [session] = await db\n .insert(sessionTable)\n .values({\n id: sessionId,\n expiresAt: new Date(addDays(new Date(), 7)),\n active_organization_id: flags.activeOrganizationId,\n userId: userId,\n })\n .returning();\n\n return session;\n}\n\n/**\n * Signs the user out and redirects to the sign-in page.\n */\nexport async function sessionSignOut() {\n const { session } = await getCurrentSession();\n\n if (session) {\n await invalidateSession(session.id);\n await deleteSessionTokenCookie();\n }\n\n redirect(\"/signin\");\n}\n\n/**\n * Get all active sessions for a user.\n */\nexport async function getUserSessions(\n userId: string,\n currentSessionId: string,\n): Promise<UserSession[]> {\n const sessions = await db\n .select()\n .from(sessionTable)\n .where(eq(sessionTable.userId, userId));\n\n return sessions.map((session) => ({\n id: session.id,\n createdAt: session.createdAt,\n expiresAt: session.expiresAt,\n isCurrent: session.id === currentSessionId,\n }));\n}\n\n/**\n * Invalidate all sessions for a user except the specified current one.\n */\nexport async function invalidateOtherSessions(\n userId: string,\n currentSessionId: string,\n): Promise<void> {\n await db\n .delete(sessionTable)\n .where(\n and(\n eq(sessionTable.userId, userId),\n ne(sessionTable.id, currentSessionId),\n ),\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"session.mjs","names":[],"sources":["../../../src/core/auth/session.ts"],"sourcesContent":["\"use server\";\n\nimport { sha256 } from \"@oslojs/crypto/sha2\";\nimport {\n encodeBase32LowerCaseNoPadding,\n encodeHexLowerCase,\n} from \"@oslojs/encoding\";\nimport { addDays } from \"date-fns\";\nimport { and, eq, ne } from \"drizzle-orm\";\nimport { cookies, headers } from \"next/headers\";\nimport { redirect } from \"next/navigation\";\nimport { db } from \"../../server/database/inject\";\nimport { sessionTable, userTable } from \"../../server/database/schema\";\nimport { augmentSession } from \"./augment\";\nimport { performFullUserAugmentation } from \"./logic\";\n\nimport type {\n AuthSession,\n Session,\n SessionFlags,\n User,\n UserSession,\n} from \"./types\";\n\n/**\n * Returns the user's IP address.\n */\nexport async function getIPAddress(): Promise<string | null> {\n return (await headers()).get(\"x-forwarded-for\");\n}\n\n/**\n * Validates the session token.\n */\nexport async function validateSessionToken(\n token: string,\n): Promise<AuthSession> {\n const sessionId = encodeHexLowerCase(sha256(new TextEncoder().encode(token)));\n\n const [row] = await db\n .select({\n session: sessionTable,\n user: userTable,\n })\n .from(sessionTable)\n .innerJoin(userTable, eq(sessionTable.userId, userTable.id))\n .where(eq(sessionTable.id, sessionId));\n\n if (!row || !row.user) {\n return { session: null, user: null };\n }\n\n const { session: baseSession, user: baseUser } = row;\n\n // STRICTLY remove non-serializable and sensitive fields\n const { password, recovery_code, ...safeUser } = baseUser;\n\n // Check if session is expired\n if (new Date() > baseSession.expiresAt) {\n await db.delete(sessionTable).where(eq(sessionTable.id, baseSession.id));\n return { session: null, user: null };\n }\n\n // AUGMENT (EXTENSIBILITY POINTS)\n const augmentedUser = await performFullUserAugmentation(safeUser as User);\n const augmentedSession = await augmentSession(baseSession as Session);\n\n // ENSURE PLAIN OBJECTS for Client Components\n return {\n session: augmentedSession ? { ...augmentedSession } : null,\n user: augmentedUser ? { ...augmentedUser } : null,\n };\n}\n\n/**\n * Returns the current user session from cookies.\n */\nexport const getCurrentSession = async (): Promise<AuthSession> => {\n const cookieStore = await cookies();\n const token = cookieStore.get(\"session\")?.value ?? null;\n\n if (token === null) {\n return { session: null, user: null };\n }\n\n return await validateSessionToken(token);\n};\n\n/**\n * Invalidates a single session.\n */\nexport async function invalidateSession(sessionId: string): Promise<void> {\n await db.delete(sessionTable).where(eq(sessionTable.id, sessionId));\n}\n\n/**\n * Invalidates all user sessions.\n */\nexport async function invalidateUserSessions(userId: string): Promise<void> {\n await db.delete(sessionTable).where(eq(sessionTable.userId, userId));\n}\n\n/**\n * Sets the session token in a cookie.\n */\nexport async function setSessionTokenCookie(\n token: string,\n expiresAt: Date,\n): Promise<void> {\n const cookieStore = await cookies();\n cookieStore.set(\"session\", token, {\n httpOnly: true,\n path: \"/\",\n secure: process.env.NODE_ENV === \"production\",\n sameSite: \"lax\",\n expires: expiresAt,\n });\n}\n\n/**\n * Removes the session token cookie.\n */\nexport async function deleteSessionTokenCookie(): Promise<void> {\n const cookieStore = await cookies();\n cookieStore.delete(\"session\");\n}\n\n/**\n * Generates a new random session token.\n */\nexport async function generateSessionToken(): Promise<string> {\n const tokenBytes = new Uint8Array(20);\n crypto.getRandomValues(tokenBytes);\n return encodeBase32LowerCaseNoPadding(tokenBytes).toLowerCase();\n}\n\n/**\n * Creates a new session in the database.\n */\nexport async function createSession(\n token: string,\n userId: string,\n flags: SessionFlags,\n): Promise<Session> {\n const sessionId = encodeHexLowerCase(sha256(new TextEncoder().encode(token)));\n\n const [session] = await db\n .insert(sessionTable)\n .values({\n id: sessionId,\n expiresAt: new Date(addDays(new Date(), 7)),\n active_organization_id: flags.activeOrganizationId,\n userId: userId,\n })\n .returning();\n\n return session;\n}\n\n/**\n * Signs the user out and redirects to the sign-in page.\n */\nexport async function sessionSignOut() {\n const { session } = await getCurrentSession();\n\n if (session) {\n await invalidateSession(session.id);\n await deleteSessionTokenCookie();\n }\n\n redirect(\"/signin\");\n}\n\n/**\n * Get all active sessions for a user.\n */\nexport async function getUserSessions(\n userId: string,\n currentSessionId: string,\n): Promise<UserSession[]> {\n const sessions = await db\n .select()\n .from(sessionTable)\n .where(eq(sessionTable.userId, userId));\n\n return sessions.map((session) => ({\n id: session.id,\n createdAt: session.createdAt,\n expiresAt: session.expiresAt,\n isCurrent: session.id === currentSessionId,\n }));\n}\n\n/**\n * Invalidate all sessions for a user except the specified current one.\n */\nexport async function invalidateOtherSessions(\n userId: string,\n currentSessionId: string,\n): Promise<void> {\n await db\n .delete(sessionTable)\n .where(\n and(\n eq(sessionTable.userId, userId),\n ne(sessionTable.id, currentSessionId),\n ),\n );\n}\n"],"mappings":"sjBA2BA,eAAsB,GAAuC,CAC3D,OAAQ,MAAM,GAAS,EAAE,IAAI,kBAAkB,CAMjD,eAAsB,EACpB,EACsB,CACtB,IAAM,EAAY,EAAmB,EAAO,IAAI,aAAa,CAAC,OAAO,EAAM,CAAC,CAAC,CAEvE,CAAC,GAAO,MAAM,EACjB,OAAO,CACN,QAAS,EACT,KAAM,EACP,CAAC,CACD,KAAK,EAAa,CAClB,UAAU,EAAW,EAAG,EAAa,OAAQ,EAAU,GAAG,CAAC,CAC3D,MAAM,EAAG,EAAa,GAAI,EAAU,CAAC,CAExC,GAAI,CAAC,GAAO,CAAC,EAAI,KACf,MAAO,CAAE,QAAS,KAAM,KAAM,KAAM,CAGtC,GAAM,CAAE,QAAS,EAAa,KAAM,GAAa,EAG3C,CAAE,WAAU,gBAAe,GAAG,GAAa,EAGjD,GAAI,IAAI,KAAS,EAAY,UAE3B,OADA,MAAM,EAAG,OAAO,EAAa,CAAC,MAAM,EAAG,EAAa,GAAI,EAAY,GAAG,CAAC,CACjE,CAAE,QAAS,KAAM,KAAM,KAAM,CAItC,IAAM,EAAgB,MAAM,EAA4B,EAAiB,CACnE,EAAmB,MAAM,EAAe,EAAuB,CAGrE,MAAO,CACL,QAAS,EAAmB,CAAE,GAAG,EAAkB,CAAG,KACtD,KAAM,EAAgB,CAAE,GAAG,EAAe,CAAG,KAC9C,CAMH,MAAa,EAAoB,SAAkC,CAEjE,IAAM,GADc,MAAM,GAAS,EACT,IAAI,UAAU,EAAE,OAAS,KAMnD,OAJI,IAAU,KACL,CAAE,QAAS,KAAM,KAAM,KAAM,CAG/B,MAAM,EAAqB,EAAM,EAM1C,eAAsB,EAAkB,EAAkC,CACxE,MAAM,EAAG,OAAO,EAAa,CAAC,MAAM,EAAG,EAAa,GAAI,EAAU,CAAC,CAMrE,eAAsB,EAAuB,EAA+B,CAC1E,MAAM,EAAG,OAAO,EAAa,CAAC,MAAM,EAAG,EAAa,OAAQ,EAAO,CAAC,CAMtE,eAAsB,EACpB,EACA,EACe,EACK,MAAM,GAAS,EACvB,IAAI,UAAW,EAAO,CAChC,SAAU,GACV,KAAM,IACN,OAAQ,QAAQ,IAAI,WAAa,aACjC,SAAU,MACV,QAAS,EACV,CAAC,CAMJ,eAAsB,GAA0C,EAC1C,MAAM,GAAS,EACvB,OAAO,UAAU,CAM/B,eAAsB,GAAwC,CAC5D,IAAM,EAAa,IAAI,WAAW,GAAG,CAErC,OADA,OAAO,gBAAgB,EAAW,CAC3B,EAA+B,EAAW,CAAC,aAAa,CAMjE,eAAsB,EACpB,EACA,EACA,EACkB,CAClB,IAAM,EAAY,EAAmB,EAAO,IAAI,aAAa,CAAC,OAAO,EAAM,CAAC,CAAC,CAEvE,CAAC,GAAW,MAAM,EACrB,OAAO,EAAa,CACpB,OAAO,CACN,GAAI,EACJ,UAAW,IAAI,KAAK,EAAQ,IAAI,KAAQ,EAAE,CAAC,CAC3C,uBAAwB,EAAM,qBACtB,SACT,CAAC,CACD,WAAW,CAEd,OAAO,EAMT,eAAsB,GAAiB,CACrC,GAAM,CAAE,WAAY,MAAM,GAAmB,CAEzC,IACF,MAAM,EAAkB,EAAQ,GAAG,CACnC,MAAM,GAA0B,EAGlC,EAAS,UAAU,CAMrB,eAAsB,EACpB,EACA,EACwB,CAMxB,OALiB,MAAM,EACpB,QAAQ,CACR,KAAK,EAAa,CAClB,MAAM,EAAG,EAAa,OAAQ,EAAO,CAAC,EAEzB,IAAK,IAAa,CAChC,GAAI,EAAQ,GACZ,UAAW,EAAQ,UACnB,UAAW,EAAQ,UACnB,UAAW,EAAQ,KAAO,EAC3B,EAAE,CAML,eAAsB,EACpB,EACA,EACe,CACf,MAAM,EACH,OAAO,EAAa,CACpB,MACC,EACE,EAAG,EAAa,OAAQ,EAAO,CAC/B,EAAG,EAAa,GAAI,EAAiB,CACtC,CACF"}
@@ -1 +1 @@
1
- {"version":3,"file":"bootstrap.mjs","names":[],"sources":["../../src/core/bootstrap.ts"],"sourcesContent":["import { injectDb } from \"../server/database/inject.js\";\nimport { initEmailVerification } from \"./auth/email-verification.js\";\nimport { eventBus } from \"./event-bus.js\";\nimport { filesystemService } from \"./filesystem/index.js\";\nimport { LocalFileProvider } from \"./filesystem/providers/local.js\";\nimport { notificationService } from \"./notifications/service.js\";\nimport { isSystemInstalled } from \"./setup.js\";\n\nexport async function ensureSystemInitialized(providedDb?: any) {\n if (typeof window !== \"undefined\") return;\n\n const g = globalThis as any;\n\n // 1. Immediate injection if provided\n if (providedDb) {\n injectDb(providedDb);\n }\n\n // 2. Prevent infinite recursion and double initialization\n if (g.__KRYO_INITIALIZED__) return;\n if (g.__KRYO_INITIALIZING__) return; // Already in progress, don't block/deadlock\n\n g.__KRYO_INITIALIZING__ = true;\n\n try {\n console.log(\"[Kryo:Bootstrap] Starting system initialization...\");\n\n // Check if we have DB after any possible injection\n if (!g.__KRYO_DB__) {\n console.warn(\n \"[Kryo:Bootstrap] DB not detected during bootstrap start. Trying to continue...\",\n );\n }\n\n // Check if system is installed before initializing modules\n if (await isSystemInstalled()) {\n // Order matters: services first, then modules (which use services)\n notificationService.init();\n await initEmailVerification();\n\n // Auto-register local filesystem provider as a fallback\n const local = new LocalFileProvider();\n\n filesystemService.registerProvider(local);\n filesystemService.setDefaultProvider(local.id);\n } else {\n console.log(\n \"[Kryo:Bootstrap] System not installed. Skipping module initialization.\",\n );\n }\n\n await eventBus.publish(\"system:start\", { runtime: \"nodejs\" });\n\n console.log(\"[Kryo:Bootstrap] System initialized successfully.\");\n g.__KRYO_INITIALIZED__ = true;\n } catch (error) {\n console.error(\"[Kryo:Bootstrap] Initialization failed:\", error);\n } finally {\n g.__KRYO_INITIALIZING__ = false;\n }\n}\n"],"mappings":"6bAQA,eAAsB,EAAwB,EAAkB,CAC9D,GAAI,OAAO,OAAW,IAAa,OAEnC,IAAM,EAAI,WAGV,GAAI,GACF,EAAS,EAAW,CAIlB,GAAE,sBACF,GAAE,sBAEN,GAAE,sBAAwB,GAE1B,GAAI,CAWF,GAVA,QAAQ,IAAI,qDAAqD,CAG5D,EAAE,aACL,QAAQ,KACN,iFACD,CAIC,MAAM,GAAmB,CAAE,CAE7B,EAAoB,MAAM,CAC1B,MAAM,GAAuB,CAG7B,IAAM,EAAQ,IAAI,EAElB,EAAkB,iBAAiB,EAAM,CACzC,EAAkB,mBAAmB,EAAM,GAAG,MAE9C,QAAQ,IACN,yEACD,CAGH,MAAM,EAAS,QAAQ,eAAgB,CAAE,QAAS,SAAU,CAAC,CAE7D,QAAQ,IAAI,oDAAoD,CAChE,EAAE,qBAAuB,SAClB,EAAO,CACd,QAAQ,MAAM,0CAA2C,EAAM,QACvD,CACR,EAAE,sBAAwB"}
1
+ {"version":3,"file":"bootstrap.mjs","names":[],"sources":["../../src/core/bootstrap.ts"],"sourcesContent":["import { injectDb } from \"../server/database/inject\";\nimport { initEmailVerification } from \"./auth/email-verification\";\nimport { eventBus } from \"./event-bus\";\nimport { filesystemService } from \"./filesystem/index\";\nimport { LocalFileProvider } from \"./filesystem/providers/local\";\nimport { notificationService } from \"./notifications/service\";\nimport { isSystemInstalled } from \"./setup\";\n\nexport async function ensureSystemInitialized(providedDb?: any) {\n if (typeof window !== \"undefined\") return;\n\n const g = globalThis as any;\n\n // 1. Immediate injection if provided\n if (providedDb) {\n injectDb(providedDb);\n }\n\n // 2. Prevent infinite recursion and double initialization\n if (g.__KRYO_INITIALIZED__) return;\n if (g.__KRYO_INITIALIZING__) return; // Already in progress, don't block/deadlock\n\n g.__KRYO_INITIALIZING__ = true;\n\n try {\n console.log(\"[Kryo:Bootstrap] Starting system initialization...\");\n\n // Check if we have DB after any possible injection\n if (!g.__KRYO_DB__) {\n console.warn(\n \"[Kryo:Bootstrap] DB not detected during bootstrap start. Trying to continue...\",\n );\n }\n\n // Check if system is installed before initializing modules\n if (await isSystemInstalled()) {\n // Order matters: services first, then modules (which use services)\n notificationService.init();\n await initEmailVerification();\n\n // Auto-register local filesystem provider as a fallback\n const local = new LocalFileProvider();\n\n filesystemService.registerProvider(local);\n filesystemService.setDefaultProvider(local.id);\n } else {\n console.log(\n \"[Kryo:Bootstrap] System not installed. Skipping module initialization.\",\n );\n }\n\n await eventBus.publish(\"system:start\", { runtime: \"nodejs\" });\n\n console.log(\"[Kryo:Bootstrap] System initialized successfully.\");\n g.__KRYO_INITIALIZED__ = true;\n } catch (error) {\n console.error(\"[Kryo:Bootstrap] Initialization failed:\", error);\n } finally {\n g.__KRYO_INITIALIZING__ = false;\n }\n}\n"],"mappings":"6bAQA,eAAsB,EAAwB,EAAkB,CAC9D,GAAI,OAAO,OAAW,IAAa,OAEnC,IAAM,EAAI,WAGV,GAAI,GACF,EAAS,EAAW,CAIlB,GAAE,sBACF,GAAE,sBAEN,GAAE,sBAAwB,GAE1B,GAAI,CAWF,GAVA,QAAQ,IAAI,qDAAqD,CAG5D,EAAE,aACL,QAAQ,KACN,iFACD,CAIC,MAAM,GAAmB,CAAE,CAE7B,EAAoB,MAAM,CAC1B,MAAM,GAAuB,CAG7B,IAAM,EAAQ,IAAI,EAElB,EAAkB,iBAAiB,EAAM,CACzC,EAAkB,mBAAmB,EAAM,GAAG,MAE9C,QAAQ,IACN,yEACD,CAGH,MAAM,EAAS,QAAQ,eAAgB,CAAE,QAAS,SAAU,CAAC,CAE7D,QAAQ,IAAI,oDAAoD,CAChE,EAAE,qBAAuB,SAClB,EAAO,CACd,QAAQ,MAAM,0CAA2C,EAAM,QACvD,CACR,EAAE,sBAAwB"}
@@ -1 +1 @@
1
- const e=require(`../_virtual/_rolldown/runtime.cjs`),t=require(`./config.cjs`);let n=require(`node:fs`);n=e.__toESM(n);let r=require(`node:path`);r=e.__toESM(r);let i=null;async function a(){if(typeof window<`u`)throw Error(`getKryoConfig can only be called on the server.`);if(process.env.NODE_ENV===`production`&&i)return i;let e=process.cwd(),a=r.default.join(e,`kryo.config.ts`),o=r.default.join(e,`kryo.config.js`),s=r.default.join(e,`kryo.config.mjs`),c={};try{let{createJiti:t}=await import(`jiti`),r=t(e,{fsCache:!1,moduleCache:!1});if(n.default.existsSync(a)){let e=`?t=${Date.now()}`,t=await r.import(`file://${a}${e}`,{default:!0});c=t.default||t}else if(n.default.existsSync(s)){let e=await r.import(s,{default:!0});c=e.default||e}else if(n.default.existsSync(o)){let e=await r.import(o,{default:!0});c=e.default||e}}catch(e){console.warn(`[Kryo:Config] Could not load kryo.config.ts, using defaults.`,e)}let l={...t.DEFAULT_CONFIG,...c};return process.env.NODE_ENV===`production`&&(i=l),l}async function o(){let e=(await a()).modulesDirectory||`modules`;return r.default.isAbsolute(e)?e:r.default.join(process.cwd(),e)}exports.getKryoConfig=a,exports.getModulesDir=o;
1
+ const e=require(`../_virtual/_rolldown/runtime.cjs`),t=require(`./config.cjs`);let n=require(`node:fs`);n=e.__toESM(n);let r=require(`node:path`);r=e.__toESM(r);let i=null;async function a(){if(typeof window<`u`)throw Error(`getKryoConfig can only be called on the server.`);if(process.env.NODE_ENV===`production`&&i)return i;let e=process.cwd(),a=r.default.join(e,`kryo.config.ts`),o=r.default.join(e,`kryo.config`),s=r.default.join(e,`kryo.config.mjs`),c={};try{let{createJiti:t}=await import(`jiti`),r=t(e,{fsCache:!1,moduleCache:!1});if(n.default.existsSync(a)){let e=`?t=${Date.now()}`,t=await r.import(`file://${a}${e}`,{default:!0});c=t.default||t}else if(n.default.existsSync(s)){let e=await r.import(s,{default:!0});c=e.default||e}else if(n.default.existsSync(o)){let e=await r.import(o,{default:!0});c=e.default||e}}catch(e){console.warn(`[Kryo:Config] Could not load kryo.config.ts, using defaults.`,e)}let l={...t.DEFAULT_CONFIG,...c};return process.env.NODE_ENV===`production`&&(i=l),l}async function o(){let e=(await a()).modulesDirectory||`modules`;return r.default.isAbsolute(e)?e:r.default.join(process.cwd(),e)}exports.getKryoConfig=a,exports.getModulesDir=o;
@@ -2,7 +2,7 @@ import { KryoConfig } from "./config.cjs";
2
2
 
3
3
  //#region src/core/config.server.d.ts
4
4
  /**
5
- * Loads the Kryo configuration from kryo.config.ts or kryo.config.js in the current working directory.
5
+ * Loads the Kryo configuration from kryo.config.ts or kryo.config in the current working directory.
6
6
  * SERVER ONLY.
7
7
  */
8
8
  declare function getKryoConfig(): Promise<KryoConfig>;
@@ -2,7 +2,7 @@ import { KryoConfig } from "./config.mjs";
2
2
 
3
3
  //#region src/core/config.server.d.ts
4
4
  /**
5
- * Loads the Kryo configuration from kryo.config.ts or kryo.config.js in the current working directory.
5
+ * Loads the Kryo configuration from kryo.config.ts or kryo.config in the current working directory.
6
6
  * SERVER ONLY.
7
7
  */
8
8
  declare function getKryoConfig(): Promise<KryoConfig>;
@@ -1,2 +1,2 @@
1
- import{DEFAULT_CONFIG as e}from"./config.mjs";import t from"node:fs";import n from"node:path";let r=null;async function i(){if(typeof window<`u`)throw Error(`getKryoConfig can only be called on the server.`);if(process.env.NODE_ENV===`production`&&r)return r;let i=process.cwd(),a=n.join(i,`kryo.config.ts`),o=n.join(i,`kryo.config.js`),s=n.join(i,`kryo.config.mjs`),c={};try{let{createJiti:e}=await import(`jiti`),n=e(i,{fsCache:!1,moduleCache:!1});if(t.existsSync(a)){let e=`?t=${Date.now()}`,t=await n.import(`file://${a}${e}`,{default:!0});c=t.default||t}else if(t.existsSync(s)){let e=await n.import(s,{default:!0});c=e.default||e}else if(t.existsSync(o)){let e=await n.import(o,{default:!0});c=e.default||e}}catch(e){console.warn(`[Kryo:Config] Could not load kryo.config.ts, using defaults.`,e)}let l={...e,...c};return process.env.NODE_ENV===`production`&&(r=l),l}async function a(){let e=(await i()).modulesDirectory||`modules`;return n.isAbsolute(e)?e:n.join(process.cwd(),e)}export{i as getKryoConfig,a as getModulesDir};
1
+ import{DEFAULT_CONFIG as e}from"./config.mjs";import t from"node:fs";import n from"node:path";let r=null;async function i(){if(typeof window<`u`)throw Error(`getKryoConfig can only be called on the server.`);if(process.env.NODE_ENV===`production`&&r)return r;let i=process.cwd(),a=n.join(i,`kryo.config.ts`),o=n.join(i,`kryo.config`),s=n.join(i,`kryo.config.mjs`),c={};try{let{createJiti:e}=await import(`jiti`),n=e(i,{fsCache:!1,moduleCache:!1});if(t.existsSync(a)){let e=`?t=${Date.now()}`,t=await n.import(`file://${a}${e}`,{default:!0});c=t.default||t}else if(t.existsSync(s)){let e=await n.import(s,{default:!0});c=e.default||e}else if(t.existsSync(o)){let e=await n.import(o,{default:!0});c=e.default||e}}catch(e){console.warn(`[Kryo:Config] Could not load kryo.config.ts, using defaults.`,e)}let l={...e,...c};return process.env.NODE_ENV===`production`&&(r=l),l}async function a(){let e=(await i()).modulesDirectory||`modules`;return n.isAbsolute(e)?e:n.join(process.cwd(),e)}export{i as getKryoConfig,a as getModulesDir};
2
2
  //# sourceMappingURL=config.server.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"config.server.mjs","names":[],"sources":["../../src/core/config.server.ts"],"sourcesContent":["import fs from \"node:fs\";\nimport path from \"node:path\";\nimport { DEFAULT_CONFIG, type KryoConfig } from \"./config.js\";\n\nlet cachedConfig: KryoConfig | null = null;\n\n/**\n * Loads the Kryo configuration from kryo.config.ts or kryo.config.js in the current working directory.\n * SERVER ONLY.\n */\nexport async function getKryoConfig(): Promise<KryoConfig> {\n // Prevent this from ever running on the client even if imported\n if (typeof window !== \"undefined\") {\n throw new Error(\"getKryoConfig can only be called on the server.\");\n }\n\n // Use memory cache in production\n if (process.env.NODE_ENV === \"production\" && cachedConfig) {\n return cachedConfig;\n }\n\n const cwd = process.cwd();\n const configPathTS = path.join(cwd, \"kryo.config.ts\");\n const configPathJS = path.join(cwd, \"kryo.config.js\");\n const configPathMJS = path.join(cwd, \"kryo.config.mjs\");\n\n let loadedConfig: Partial<KryoConfig> = {};\n\n try {\n // Dynamic import jiti only on server to avoid bundling issues\n const { createJiti } = await import(\"jiti\");\n const jiti = createJiti(cwd, {\n fsCache: false,\n moduleCache: false,\n });\n\n if (fs.existsSync(configPathTS)) {\n const cacheBuster = `?t=${Date.now()}`;\n const imported: any = await jiti.import(\n `file://${configPathTS}${cacheBuster}`,\n { default: true },\n );\n loadedConfig = imported.default || imported;\n } else if (fs.existsSync(configPathMJS)) {\n const imported: any = await jiti.import(configPathMJS, { default: true });\n loadedConfig = imported.default || imported;\n } else if (fs.existsSync(configPathJS)) {\n const imported: any = await jiti.import(configPathJS, { default: true });\n loadedConfig = imported.default || imported;\n }\n } catch (error) {\n console.warn(\n \"[Kryo:Config] Could not load kryo.config.ts, using defaults.\",\n error,\n );\n }\n\n const finalConfig = { ...DEFAULT_CONFIG, ...loadedConfig };\n\n if (process.env.NODE_ENV === \"production\") {\n cachedConfig = finalConfig;\n }\n\n return finalConfig;\n}\n\n/**\n * Helper returning the absolute path to the modules directory.\n * SERVER ONLY.\n */\nexport async function getModulesDir(): Promise<string> {\n const config = await getKryoConfig();\n\n const dir = config.modulesDirectory || \"modules\";\n return path.isAbsolute(dir) ? dir : path.join(process.cwd(), dir);\n}\n"],"mappings":"8FAIA,IAAI,EAAkC,KAMtC,eAAsB,GAAqC,CAEzD,GAAI,OAAO,OAAW,IACpB,MAAU,MAAM,kDAAkD,CAIpE,GAAI,QAAQ,IAAI,WAAa,cAAgB,EAC3C,OAAO,EAGT,IAAM,EAAM,QAAQ,KAAK,CACnB,EAAe,EAAK,KAAK,EAAK,iBAAiB,CAC/C,EAAe,EAAK,KAAK,EAAK,iBAAiB,CAC/C,EAAgB,EAAK,KAAK,EAAK,kBAAkB,CAEnD,EAAoC,EAAE,CAE1C,GAAI,CAEF,GAAM,CAAE,cAAe,MAAM,OAAO,QAC9B,EAAO,EAAW,EAAK,CAC3B,QAAS,GACT,YAAa,GACd,CAAC,CAEF,GAAI,EAAG,WAAW,EAAa,CAAE,CAC/B,IAAM,EAAc,MAAM,KAAK,KAAK,GAC9B,EAAgB,MAAM,EAAK,OAC/B,UAAU,IAAe,IACzB,CAAE,QAAS,GAAM,CAClB,CACD,EAAe,EAAS,SAAW,UAC1B,EAAG,WAAW,EAAc,CAAE,CACvC,IAAM,EAAgB,MAAM,EAAK,OAAO,EAAe,CAAE,QAAS,GAAM,CAAC,CACzE,EAAe,EAAS,SAAW,UAC1B,EAAG,WAAW,EAAa,CAAE,CACtC,IAAM,EAAgB,MAAM,EAAK,OAAO,EAAc,CAAE,QAAS,GAAM,CAAC,CACxE,EAAe,EAAS,SAAW,SAE9B,EAAO,CACd,QAAQ,KACN,+DACA,EACD,CAGH,IAAM,EAAc,CAAE,GAAG,EAAgB,GAAG,EAAc,CAM1D,OAJI,QAAQ,IAAI,WAAa,eAC3B,EAAe,GAGV,EAOT,eAAsB,GAAiC,CAGrD,IAAM,GAFS,MAAM,GAAe,EAEjB,kBAAoB,UACvC,OAAO,EAAK,WAAW,EAAI,CAAG,EAAM,EAAK,KAAK,QAAQ,KAAK,CAAE,EAAI"}
1
+ {"version":3,"file":"config.server.mjs","names":[],"sources":["../../src/core/config.server.ts"],"sourcesContent":["import fs from \"node:fs\";\nimport path from \"node:path\";\nimport { DEFAULT_CONFIG, type KryoConfig } from \"./config\";\n\nlet cachedConfig: KryoConfig | null = null;\n\n/**\n * Loads the Kryo configuration from kryo.config.ts or kryo.config in the current working directory.\n * SERVER ONLY.\n */\nexport async function getKryoConfig(): Promise<KryoConfig> {\n // Prevent this from ever running on the client even if imported\n if (typeof window !== \"undefined\") {\n throw new Error(\"getKryoConfig can only be called on the server.\");\n }\n\n // Use memory cache in production\n if (process.env.NODE_ENV === \"production\" && cachedConfig) {\n return cachedConfig;\n }\n\n const cwd = process.cwd();\n const configPathTS = path.join(cwd, \"kryo.config.ts\");\n const configPathJS = path.join(cwd, \"kryo.config\");\n const configPathMJS = path.join(cwd, \"kryo.config.mjs\");\n\n let loadedConfig: Partial<KryoConfig> = {};\n\n try {\n // Dynamic import jiti only on server to avoid bundling issues\n const { createJiti } = await import(\"jiti\");\n const jiti = createJiti(cwd, {\n fsCache: false,\n moduleCache: false,\n });\n\n if (fs.existsSync(configPathTS)) {\n const cacheBuster = `?t=${Date.now()}`;\n const imported: any = await jiti.import(\n `file://${configPathTS}${cacheBuster}`,\n { default: true },\n );\n loadedConfig = imported.default || imported;\n } else if (fs.existsSync(configPathMJS)) {\n const imported: any = await jiti.import(configPathMJS, { default: true });\n loadedConfig = imported.default || imported;\n } else if (fs.existsSync(configPathJS)) {\n const imported: any = await jiti.import(configPathJS, { default: true });\n loadedConfig = imported.default || imported;\n }\n } catch (error) {\n console.warn(\n \"[Kryo:Config] Could not load kryo.config.ts, using defaults.\",\n error,\n );\n }\n\n const finalConfig = { ...DEFAULT_CONFIG, ...loadedConfig };\n\n if (process.env.NODE_ENV === \"production\") {\n cachedConfig = finalConfig;\n }\n\n return finalConfig;\n}\n\n/**\n * Helper returning the absolute path to the modules directory.\n * SERVER ONLY.\n */\nexport async function getModulesDir(): Promise<string> {\n const config = await getKryoConfig();\n\n const dir = config.modulesDirectory || \"modules\";\n return path.isAbsolute(dir) ? dir : path.join(process.cwd(), dir);\n}\n"],"mappings":"8FAIA,IAAI,EAAkC,KAMtC,eAAsB,GAAqC,CAEzD,GAAI,OAAO,OAAW,IACpB,MAAU,MAAM,kDAAkD,CAIpE,GAAI,QAAQ,IAAI,WAAa,cAAgB,EAC3C,OAAO,EAGT,IAAM,EAAM,QAAQ,KAAK,CACnB,EAAe,EAAK,KAAK,EAAK,iBAAiB,CAC/C,EAAe,EAAK,KAAK,EAAK,cAAc,CAC5C,EAAgB,EAAK,KAAK,EAAK,kBAAkB,CAEnD,EAAoC,EAAE,CAE1C,GAAI,CAEF,GAAM,CAAE,cAAe,MAAM,OAAO,QAC9B,EAAO,EAAW,EAAK,CAC3B,QAAS,GACT,YAAa,GACd,CAAC,CAEF,GAAI,EAAG,WAAW,EAAa,CAAE,CAC/B,IAAM,EAAc,MAAM,KAAK,KAAK,GAC9B,EAAgB,MAAM,EAAK,OAC/B,UAAU,IAAe,IACzB,CAAE,QAAS,GAAM,CAClB,CACD,EAAe,EAAS,SAAW,UAC1B,EAAG,WAAW,EAAc,CAAE,CACvC,IAAM,EAAgB,MAAM,EAAK,OAAO,EAAe,CAAE,QAAS,GAAM,CAAC,CACzE,EAAe,EAAS,SAAW,UAC1B,EAAG,WAAW,EAAa,CAAE,CACtC,IAAM,EAAgB,MAAM,EAAK,OAAO,EAAc,CAAE,QAAS,GAAM,CAAC,CACxE,EAAe,EAAS,SAAW,SAE9B,EAAO,CACd,QAAQ,KACN,+DACA,EACD,CAGH,IAAM,EAAc,CAAE,GAAG,EAAgB,GAAG,EAAc,CAM1D,OAJI,QAAQ,IAAI,WAAa,eAC3B,EAAe,GAGV,EAOT,eAAsB,GAAiC,CAGrD,IAAM,GAFS,MAAM,GAAe,EAEjB,kBAAoB,UACvC,OAAO,EAAK,WAAW,EAAI,CAAG,EAAM,EAAK,KAAK,QAAQ,KAAK,CAAE,EAAI"}
@@ -1 +1 @@
1
- {"version":3,"file":"event-bus.d.cts","names":[],"sources":["../../src/core/event-bus.ts"],"mappings":";;;;;AAA4D;cAKtD,QAAA;EAAA,QACI,QAAA;EAED,SAAA,GAAA,CACL,SAAA,UACA,YAAA,UACA,OAAA,EAAS,YAAA,CAAa,CAAA;EAYjB,WAAA,CAAY,SAAA,UAAmB,YAAA;EAO/B,QAAA,CAAA;EAKM,OAAA,GAAA,CACX,SAAA,UACA,OAAA,EAAS,CAAA,EACT,MAAA,YACC,OAAA;AAAA;AAAA,cAkCQ,QAAA,EAAQ,QAAA"}
1
+ {"version":3,"file":"event-bus.d.cts","names":[],"sources":["../../src/core/event-bus.ts"],"mappings":";;;;;AAAyD;cAKnD,QAAA;EAAA,QACI,QAAA;EAED,SAAA,GAAA,CACL,SAAA,UACA,YAAA,UACA,OAAA,EAAS,YAAA,CAAa,CAAA;EAYjB,WAAA,CAAY,SAAA,UAAmB,YAAA;EAO/B,QAAA,CAAA;EAKM,OAAA,GAAA,CACX,SAAA,UACA,OAAA,EAAS,CAAA,EACT,MAAA,YACC,OAAA;AAAA;AAAA,cAkCQ,QAAA,EAAQ,QAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"event-bus.d.mts","names":[],"sources":["../../src/core/event-bus.ts"],"mappings":";;;;;AAA4D;cAKtD,QAAA;EAAA,QACI,QAAA;EAED,SAAA,GAAA,CACL,SAAA,UACA,YAAA,UACA,OAAA,EAAS,YAAA,CAAa,CAAA;EAYjB,WAAA,CAAY,SAAA,UAAmB,YAAA;EAO/B,QAAA,CAAA;EAKM,OAAA,GAAA,CACX,SAAA,UACA,OAAA,EAAS,CAAA,EACT,MAAA,YACC,OAAA;AAAA;AAAA,cAkCQ,QAAA,EAAQ,QAAA"}
1
+ {"version":3,"file":"event-bus.d.mts","names":[],"sources":["../../src/core/event-bus.ts"],"mappings":";;;;;AAAyD;cAKnD,QAAA;EAAA,QACI,QAAA;EAED,SAAA,GAAA,CACL,SAAA,UACA,YAAA,UACA,OAAA,EAAS,YAAA,CAAa,CAAA;EAYjB,WAAA,CAAY,SAAA,UAAmB,YAAA;EAO/B,QAAA,CAAA;EAKM,OAAA,GAAA,CACX,SAAA,UACA,OAAA,EAAS,CAAA,EACT,MAAA,YACC,OAAA;AAAA;AAAA,cAkCQ,QAAA,EAAQ,QAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"event-bus.mjs","names":[],"sources":["../../src/core/event-bus.ts"],"sourcesContent":["import type { EventHandler, SystemEvent } from \"./types.js\";\n\n/**\n * EventBus (Pub/Sub) - Global Singleton Pattern.\n */\nclass EventBus {\n private handlers: Map<string, Map<string, EventHandler>> = new Map();\n\n public subscribe<T>(\n eventType: string,\n subscriberId: string,\n handler: EventHandler<T>,\n ): void {\n if (!this.handlers.has(eventType)) {\n this.handlers.set(eventType, new Map());\n }\n const eventHandlers = this.handlers.get(eventType);\n eventHandlers?.set(subscriberId, handler);\n console.log(\n `[Event Bus] Subscriber \"${subscriberId}\" added for \"${eventType}\"`,\n );\n }\n\n public unsubscribe(eventType: string, subscriberId: string): void {\n this.handlers.get(eventType)?.delete(subscriberId);\n console.log(\n `[Event Bus] Subscriber \"${subscriberId}\" removed from \"${eventType}\"`,\n );\n }\n\n public clearAll(): void {\n console.log(\"[Event Bus] Resetting all listeners...\");\n this.handlers.clear();\n }\n\n public async publish<T>(\n eventType: string,\n payload: T,\n source: string = \"system\",\n ): Promise<void> {\n const eventHandlers = this.handlers.get(eventType);\n if (!eventHandlers) return;\n\n const handlers = Array.from(eventHandlers.values());\n\n console.log(\n `[Event Bus] Publishing \"${eventType}\" to ${handlers.length} subscribers`,\n );\n\n const event: SystemEvent<T> = {\n type: eventType,\n payload,\n timestamp: Date.now(),\n source,\n };\n\n await Promise.all(\n handlers.map(async (handler) => {\n try {\n await handler(event);\n } catch (error) {\n console.error(`[Event Bus] Handler Error for ${eventType}:`, error);\n }\n }),\n );\n }\n}\n\n// Ensure global singleton\nconst globalForEventBus = globalThis as unknown as {\n __KRYO_EVENT_BUS__: EventBus | undefined;\n};\n\nexport const eventBus = globalForEventBus.__KRYO_EVENT_BUS__ ?? new EventBus();\n\nglobalForEventBus.__KRYO_EVENT_BUS__ = eventBus;\n"],"mappings":"AAKA,IAAM,EAAN,KAAe,6BAC8C,IAAI,IAE/D,UACE,EACA,EACA,EACM,CACD,KAAK,SAAS,IAAI,EAAU,EAC/B,KAAK,SAAS,IAAI,EAAW,IAAI,IAAM,CAEnB,KAAK,SAAS,IAAI,EAAU,EACnC,IAAI,EAAc,EAAQ,CACzC,QAAQ,IACN,2BAA2B,EAAa,eAAe,EAAU,GAClE,CAGH,YAAmB,EAAmB,EAA4B,CAChE,KAAK,SAAS,IAAI,EAAU,EAAE,OAAO,EAAa,CAClD,QAAQ,IACN,2BAA2B,EAAa,kBAAkB,EAAU,GACrE,CAGH,UAAwB,CACtB,QAAQ,IAAI,yCAAyC,CACrD,KAAK,SAAS,OAAO,CAGvB,MAAa,QACX,EACA,EACA,EAAiB,SACF,CACf,IAAM,EAAgB,KAAK,SAAS,IAAI,EAAU,CAClD,GAAI,CAAC,EAAe,OAEpB,IAAM,EAAW,MAAM,KAAK,EAAc,QAAQ,CAAC,CAEnD,QAAQ,IACN,2BAA2B,EAAU,OAAO,EAAS,OAAO,cAC7D,CAED,IAAM,EAAwB,CAC5B,KAAM,EACN,UACA,UAAW,KAAK,KAAK,CACrB,SACD,CAED,MAAM,QAAQ,IACZ,EAAS,IAAI,KAAO,IAAY,CAC9B,GAAI,CACF,MAAM,EAAQ,EAAM,OACb,EAAO,CACd,QAAQ,MAAM,iCAAiC,EAAU,GAAI,EAAM,GAErE,CACH,GAKL,MAAM,EAAoB,WAIb,EAAW,EAAkB,oBAAsB,IAAI,EAEpE,EAAkB,mBAAqB"}
1
+ {"version":3,"file":"event-bus.mjs","names":[],"sources":["../../src/core/event-bus.ts"],"sourcesContent":["import type { EventHandler, SystemEvent } from \"./types\";\n\n/**\n * EventBus (Pub/Sub) - Global Singleton Pattern.\n */\nclass EventBus {\n private handlers: Map<string, Map<string, EventHandler>> = new Map();\n\n public subscribe<T>(\n eventType: string,\n subscriberId: string,\n handler: EventHandler<T>,\n ): void {\n if (!this.handlers.has(eventType)) {\n this.handlers.set(eventType, new Map());\n }\n const eventHandlers = this.handlers.get(eventType);\n eventHandlers?.set(subscriberId, handler);\n console.log(\n `[Event Bus] Subscriber \"${subscriberId}\" added for \"${eventType}\"`,\n );\n }\n\n public unsubscribe(eventType: string, subscriberId: string): void {\n this.handlers.get(eventType)?.delete(subscriberId);\n console.log(\n `[Event Bus] Subscriber \"${subscriberId}\" removed from \"${eventType}\"`,\n );\n }\n\n public clearAll(): void {\n console.log(\"[Event Bus] Resetting all listeners...\");\n this.handlers.clear();\n }\n\n public async publish<T>(\n eventType: string,\n payload: T,\n source: string = \"system\",\n ): Promise<void> {\n const eventHandlers = this.handlers.get(eventType);\n if (!eventHandlers) return;\n\n const handlers = Array.from(eventHandlers.values());\n\n console.log(\n `[Event Bus] Publishing \"${eventType}\" to ${handlers.length} subscribers`,\n );\n\n const event: SystemEvent<T> = {\n type: eventType,\n payload,\n timestamp: Date.now(),\n source,\n };\n\n await Promise.all(\n handlers.map(async (handler) => {\n try {\n await handler(event);\n } catch (error) {\n console.error(`[Event Bus] Handler Error for ${eventType}:`, error);\n }\n }),\n );\n }\n}\n\n// Ensure global singleton\nconst globalForEventBus = globalThis as unknown as {\n __KRYO_EVENT_BUS__: EventBus | undefined;\n};\n\nexport const eventBus = globalForEventBus.__KRYO_EVENT_BUS__ ?? new EventBus();\n\nglobalForEventBus.__KRYO_EVENT_BUS__ = eventBus;\n"],"mappings":"AAKA,IAAM,EAAN,KAAe,6BAC8C,IAAI,IAE/D,UACE,EACA,EACA,EACM,CACD,KAAK,SAAS,IAAI,EAAU,EAC/B,KAAK,SAAS,IAAI,EAAW,IAAI,IAAM,CAEnB,KAAK,SAAS,IAAI,EAAU,EACnC,IAAI,EAAc,EAAQ,CACzC,QAAQ,IACN,2BAA2B,EAAa,eAAe,EAAU,GAClE,CAGH,YAAmB,EAAmB,EAA4B,CAChE,KAAK,SAAS,IAAI,EAAU,EAAE,OAAO,EAAa,CAClD,QAAQ,IACN,2BAA2B,EAAa,kBAAkB,EAAU,GACrE,CAGH,UAAwB,CACtB,QAAQ,IAAI,yCAAyC,CACrD,KAAK,SAAS,OAAO,CAGvB,MAAa,QACX,EACA,EACA,EAAiB,SACF,CACf,IAAM,EAAgB,KAAK,SAAS,IAAI,EAAU,CAClD,GAAI,CAAC,EAAe,OAEpB,IAAM,EAAW,MAAM,KAAK,EAAc,QAAQ,CAAC,CAEnD,QAAQ,IACN,2BAA2B,EAAU,OAAO,EAAS,OAAO,cAC7D,CAED,IAAM,EAAwB,CAC5B,KAAM,EACN,UACA,UAAW,KAAK,KAAK,CACrB,SACD,CAED,MAAM,QAAQ,IACZ,EAAS,IAAI,KAAO,IAAY,CAC9B,GAAI,CACF,MAAM,EAAQ,EAAM,OACb,EAAO,CACd,QAAQ,MAAM,iCAAiC,EAAU,GAAI,EAAM,GAErE,CACH,GAKL,MAAM,EAAoB,WAIb,EAAW,EAAkB,oBAAsB,IAAI,EAEpE,EAAkB,mBAAqB"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../../../src/core/filesystem/index.ts"],"sourcesContent":["import { LocalFileProvider } from \"./providers/local.js\";\nimport { filesystemService } from \"./service.js\";\n\nexport * from \"./types.js\";\nexport { filesystemService };\n\n// Auto-register local provider\nif (typeof window === \"undefined\") {\n const local = new LocalFileProvider();\n\n filesystemService.registerProvider(local);\n filesystemService.setDefaultProvider(local.id);\n}\n"],"mappings":"4GAOA,GAAI,OAAO,OAAW,IAAa,CACjC,IAAM,EAAQ,IAAI,EAElB,EAAkB,iBAAiB,EAAM,CACzC,EAAkB,mBAAmB,EAAM,GAAG"}
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../../../src/core/filesystem/index.ts"],"sourcesContent":["import { LocalFileProvider } from \"./providers/local\";\nimport { filesystemService } from \"./service\";\n\nexport * from \"./types\";\nexport { filesystemService };\n\n// Auto-register local provider\nif (typeof window === \"undefined\") {\n const local = new LocalFileProvider();\n\n filesystemService.registerProvider(local);\n filesystemService.setDefaultProvider(local.id);\n}\n"],"mappings":"4GAOA,GAAI,OAAO,OAAW,IAAa,CACjC,IAAM,EAAQ,IAAI,EAElB,EAAkB,iBAAiB,EAAM,CACzC,EAAkB,mBAAmB,EAAM,GAAG"}
@@ -1 +1 @@
1
- {"version":3,"file":"local.mjs","names":[],"sources":["../../../../src/core/filesystem/providers/local.ts"],"sourcesContent":["import fs from \"node:fs\";\nimport path from \"node:path\";\nimport type { IStorageProvider, StorageResult } from \"../types.js\";\n\nexport class LocalFileProvider implements IStorageProvider {\n public id = \"local\";\n public name = \"Local Filesystem\";\n\n private generateUniqueImageName(name: string) {\n const cleanName = name.replace(/\\s+/g, \"-\").toLowerCase();\n const suffix = Math.floor(Math.random() * Date.now()).toString(36);\n const index = cleanName.lastIndexOf(\".\");\n\n return index < 0\n ? `${cleanName}-${suffix}`\n : `${cleanName.slice(0, index)}-${suffix}${cleanName.slice(index)}`;\n }\n\n public async upload(file: File): Promise<StorageResult> {\n try {\n const storageFolder = process.env.FILE_STORAGE_FOLDER ?? \"public/storage\";\n\n if (!fs.existsSync(storageFolder)) {\n fs.mkdirSync(storageFolder, { recursive: true });\n }\n\n const name = this.generateUniqueImageName(file.name);\n const filePath = path.join(storageFolder, name);\n const buffer = await file.arrayBuffer();\n fs.writeFileSync(filePath, Buffer.from(buffer));\n\n const url = `${storageFolder}/${name}`.replace(\"public\", \"\");\n\n return {\n name: name,\n url,\n type: file.type,\n size: file.size,\n service: \"local\",\n serviceId: filePath,\n };\n } catch (error) {\n console.error(\"Error uploading file\", error);\n return { error: \"Error uploading file\" };\n }\n }\n}\n"],"mappings":"gDAIA,IAAa,EAAb,KAA2D,uBAC7C,kBACE,mBAEd,wBAAgC,EAAc,CAC5C,IAAM,EAAY,EAAK,QAAQ,OAAQ,IAAI,CAAC,aAAa,CACnD,EAAS,KAAK,MAAM,KAAK,QAAQ,CAAG,KAAK,KAAK,CAAC,CAAC,SAAS,GAAG,CAC5D,EAAQ,EAAU,YAAY,IAAI,CAExC,OAAO,EAAQ,EACX,GAAG,EAAU,GAAG,IAChB,GAAG,EAAU,MAAM,EAAG,EAAM,CAAC,GAAG,IAAS,EAAU,MAAM,EAAM,GAGrE,MAAa,OAAO,EAAoC,CACtD,GAAI,CACF,IAAM,EAAgB,QAAQ,IAAI,qBAAuB,iBAEpD,EAAG,WAAW,EAAc,EAC/B,EAAG,UAAU,EAAe,CAAE,UAAW,GAAM,CAAC,CAGlD,IAAM,EAAO,KAAK,wBAAwB,EAAK,KAAK,CAC9C,EAAW,EAAK,KAAK,EAAe,EAAK,CACzC,EAAS,MAAM,EAAK,aAAa,CAKvC,OAJA,EAAG,cAAc,EAAU,OAAO,KAAK,EAAO,CAAC,CAIxC,CACC,OACN,IAJU,GAAG,EAAc,GAAG,IAAO,QAAQ,SAAU,GAAG,CAK1D,KAAM,EAAK,KACX,KAAM,EAAK,KACX,QAAS,QACT,UAAW,EACZ,OACM,EAAO,CAEd,OADA,QAAQ,MAAM,uBAAwB,EAAM,CACrC,CAAE,MAAO,uBAAwB"}
1
+ {"version":3,"file":"local.mjs","names":[],"sources":["../../../../src/core/filesystem/providers/local.ts"],"sourcesContent":["import fs from \"node:fs\";\nimport path from \"node:path\";\nimport type { IStorageProvider, StorageResult } from \"../types\";\n\nexport class LocalFileProvider implements IStorageProvider {\n public id = \"local\";\n public name = \"Local Filesystem\";\n\n private generateUniqueImageName(name: string) {\n const cleanName = name.replace(/\\s+/g, \"-\").toLowerCase();\n const suffix = Math.floor(Math.random() * Date.now()).toString(36);\n const index = cleanName.lastIndexOf(\".\");\n\n return index < 0\n ? `${cleanName}-${suffix}`\n : `${cleanName.slice(0, index)}-${suffix}${cleanName.slice(index)}`;\n }\n\n public async upload(file: File): Promise<StorageResult> {\n try {\n const storageFolder = process.env.FILE_STORAGE_FOLDER ?? \"public/storage\";\n\n if (!fs.existsSync(storageFolder)) {\n fs.mkdirSync(storageFolder, { recursive: true });\n }\n\n const name = this.generateUniqueImageName(file.name);\n const filePath = path.join(storageFolder, name);\n const buffer = await file.arrayBuffer();\n fs.writeFileSync(filePath, Buffer.from(buffer));\n\n const url = `${storageFolder}/${name}`.replace(\"public\", \"\");\n\n return {\n name: name,\n url,\n type: file.type,\n size: file.size,\n service: \"local\",\n serviceId: filePath,\n };\n } catch (error) {\n console.error(\"Error uploading file\", error);\n return { error: \"Error uploading file\" };\n }\n }\n}\n"],"mappings":"gDAIA,IAAa,EAAb,KAA2D,uBAC7C,kBACE,mBAEd,wBAAgC,EAAc,CAC5C,IAAM,EAAY,EAAK,QAAQ,OAAQ,IAAI,CAAC,aAAa,CACnD,EAAS,KAAK,MAAM,KAAK,QAAQ,CAAG,KAAK,KAAK,CAAC,CAAC,SAAS,GAAG,CAC5D,EAAQ,EAAU,YAAY,IAAI,CAExC,OAAO,EAAQ,EACX,GAAG,EAAU,GAAG,IAChB,GAAG,EAAU,MAAM,EAAG,EAAM,CAAC,GAAG,IAAS,EAAU,MAAM,EAAM,GAGrE,MAAa,OAAO,EAAoC,CACtD,GAAI,CACF,IAAM,EAAgB,QAAQ,IAAI,qBAAuB,iBAEpD,EAAG,WAAW,EAAc,EAC/B,EAAG,UAAU,EAAe,CAAE,UAAW,GAAM,CAAC,CAGlD,IAAM,EAAO,KAAK,wBAAwB,EAAK,KAAK,CAC9C,EAAW,EAAK,KAAK,EAAe,EAAK,CACzC,EAAS,MAAM,EAAK,aAAa,CAKvC,OAJA,EAAG,cAAc,EAAU,OAAO,KAAK,EAAO,CAAC,CAIxC,CACC,OACN,IAJU,GAAG,EAAc,GAAG,IAAO,QAAQ,SAAU,GAAG,CAK1D,KAAM,EAAK,KACX,KAAM,EAAK,KACX,QAAS,QACT,UAAW,EACZ,OACM,EAAO,CAEd,OADA,QAAQ,MAAM,uBAAwB,EAAM,CACrC,CAAE,MAAO,uBAAwB"}
@@ -1 +1 @@
1
- {"version":3,"file":"service.mjs","names":[],"sources":["../../../src/core/filesystem/service.ts"],"sourcesContent":["import type { IStorageProvider, StorageResult } from \"./types.js\";\n\nclass FilesystemService {\n private providers: Map<string, IStorageProvider> = new Map();\n private defaultProviderId: string | null = null;\n\n public registerProvider(provider: IStorageProvider) {\n this.providers.set(provider.id, provider);\n if (!this.defaultProviderId || this.defaultProviderId === \"local\") {\n this.defaultProviderId = provider.id;\n }\n console.log(`[Filesystem] Provider registered: ${provider.id}`);\n }\n\n public unregisterProvider(id: string) {\n this.providers.delete(id);\n if (this.defaultProviderId === id) {\n this.defaultProviderId = this.providers.has(\"local\")\n ? \"local\"\n : Array.from(this.providers.keys())[0] || null;\n }\n console.log(`[Filesystem] Provider unregistered: ${id}`);\n }\n\n public setDefaultProvider(id: string) {\n if (this.providers.has(id)) {\n this.defaultProviderId = id;\n console.log(`[Filesystem] Default provider set to: ${id}`);\n }\n }\n\n public async upload(file: File, providerId?: string): Promise<StorageResult> {\n let id = providerId || this.defaultProviderId;\n\n // Fallback logic\n if (!id || !this.providers.has(id)) {\n id = \"local\";\n }\n\n const provider = this.providers.get(id);\n\n if (!provider) {\n return { error: \"No storage provider available (local fallback failed)\" };\n }\n\n return await provider.upload(file);\n }\n\n public getProviders() {\n return Array.from(this.providers.values()).map((p) => ({\n id: p.id,\n name: p.name,\n }));\n }\n}\n\n// Global Singleton Pattern\nconst globalForFilesystem = globalThis as unknown as {\n __KRYO_FILESYSTEM_SERVICE__: FilesystemService | undefined;\n};\n\nexport const filesystemService =\n globalForFilesystem.__KRYO_FILESYSTEM_SERVICE__ ?? new FilesystemService();\n\nglobalForFilesystem.__KRYO_FILESYSTEM_SERVICE__ = filesystemService;\n"],"mappings":"AAEA,IAAM,EAAN,KAAwB,8BAC6B,IAAI,2BACZ,KAE3C,iBAAwB,EAA4B,CAClD,KAAK,UAAU,IAAI,EAAS,GAAI,EAAS,EACrC,CAAC,KAAK,mBAAqB,KAAK,oBAAsB,WACxD,KAAK,kBAAoB,EAAS,IAEpC,QAAQ,IAAI,qCAAqC,EAAS,KAAK,CAGjE,mBAA0B,EAAY,CACpC,KAAK,UAAU,OAAO,EAAG,CACrB,KAAK,oBAAsB,IAC7B,KAAK,kBAAoB,KAAK,UAAU,IAAI,QAAQ,CAChD,QACA,MAAM,KAAK,KAAK,UAAU,MAAM,CAAC,CAAC,IAAM,MAE9C,QAAQ,IAAI,uCAAuC,IAAK,CAG1D,mBAA0B,EAAY,CAChC,KAAK,UAAU,IAAI,EAAG,GACxB,KAAK,kBAAoB,EACzB,QAAQ,IAAI,yCAAyC,IAAK,EAI9D,MAAa,OAAO,EAAY,EAA6C,CAC3E,IAAI,EAAK,GAAc,KAAK,mBAGxB,CAAC,GAAM,CAAC,KAAK,UAAU,IAAI,EAAG,IAChC,EAAK,SAGP,IAAM,EAAW,KAAK,UAAU,IAAI,EAAG,CAMvC,OAJK,EAIE,MAAM,EAAS,OAAO,EAAK,CAHzB,CAAE,MAAO,wDAAyD,CAM7E,cAAsB,CACpB,OAAO,MAAM,KAAK,KAAK,UAAU,QAAQ,CAAC,CAAC,IAAK,IAAO,CACrD,GAAI,EAAE,GACN,KAAM,EAAE,KACT,EAAE,GAKP,MAAM,EAAsB,WAIf,EACX,EAAoB,6BAA+B,IAAI,EAEzD,EAAoB,4BAA8B"}
1
+ {"version":3,"file":"service.mjs","names":[],"sources":["../../../src/core/filesystem/service.ts"],"sourcesContent":["import type { IStorageProvider, StorageResult } from \"./types\";\n\nclass FilesystemService {\n private providers: Map<string, IStorageProvider> = new Map();\n private defaultProviderId: string | null = null;\n\n public registerProvider(provider: IStorageProvider) {\n this.providers.set(provider.id, provider);\n if (!this.defaultProviderId || this.defaultProviderId === \"local\") {\n this.defaultProviderId = provider.id;\n }\n console.log(`[Filesystem] Provider registered: ${provider.id}`);\n }\n\n public unregisterProvider(id: string) {\n this.providers.delete(id);\n if (this.defaultProviderId === id) {\n this.defaultProviderId = this.providers.has(\"local\")\n ? \"local\"\n : Array.from(this.providers.keys())[0] || null;\n }\n console.log(`[Filesystem] Provider unregistered: ${id}`);\n }\n\n public setDefaultProvider(id: string) {\n if (this.providers.has(id)) {\n this.defaultProviderId = id;\n console.log(`[Filesystem] Default provider set to: ${id}`);\n }\n }\n\n public async upload(file: File, providerId?: string): Promise<StorageResult> {\n let id = providerId || this.defaultProviderId;\n\n // Fallback logic\n if (!id || !this.providers.has(id)) {\n id = \"local\";\n }\n\n const provider = this.providers.get(id);\n\n if (!provider) {\n return { error: \"No storage provider available (local fallback failed)\" };\n }\n\n return await provider.upload(file);\n }\n\n public getProviders() {\n return Array.from(this.providers.values()).map((p) => ({\n id: p.id,\n name: p.name,\n }));\n }\n}\n\n// Global Singleton Pattern\nconst globalForFilesystem = globalThis as unknown as {\n __KRYO_FILESYSTEM_SERVICE__: FilesystemService | undefined;\n};\n\nexport const filesystemService =\n globalForFilesystem.__KRYO_FILESYSTEM_SERVICE__ ?? new FilesystemService();\n\nglobalForFilesystem.__KRYO_FILESYSTEM_SERVICE__ = filesystemService;\n"],"mappings":"AAEA,IAAM,EAAN,KAAwB,8BAC6B,IAAI,2BACZ,KAE3C,iBAAwB,EAA4B,CAClD,KAAK,UAAU,IAAI,EAAS,GAAI,EAAS,EACrC,CAAC,KAAK,mBAAqB,KAAK,oBAAsB,WACxD,KAAK,kBAAoB,EAAS,IAEpC,QAAQ,IAAI,qCAAqC,EAAS,KAAK,CAGjE,mBAA0B,EAAY,CACpC,KAAK,UAAU,OAAO,EAAG,CACrB,KAAK,oBAAsB,IAC7B,KAAK,kBAAoB,KAAK,UAAU,IAAI,QAAQ,CAChD,QACA,MAAM,KAAK,KAAK,UAAU,MAAM,CAAC,CAAC,IAAM,MAE9C,QAAQ,IAAI,uCAAuC,IAAK,CAG1D,mBAA0B,EAAY,CAChC,KAAK,UAAU,IAAI,EAAG,GACxB,KAAK,kBAAoB,EACzB,QAAQ,IAAI,yCAAyC,IAAK,EAI9D,MAAa,OAAO,EAAY,EAA6C,CAC3E,IAAI,EAAK,GAAc,KAAK,mBAGxB,CAAC,GAAM,CAAC,KAAK,UAAU,IAAI,EAAG,IAChC,EAAK,SAGP,IAAM,EAAW,KAAK,UAAU,IAAI,EAAG,CAMvC,OAJK,EAIE,MAAM,EAAS,OAAO,EAAK,CAHzB,CAAE,MAAO,wDAAyD,CAM7E,cAAsB,CACpB,OAAO,MAAM,KAAK,KAAK,UAAU,QAAQ,CAAC,CAAC,IAAK,IAAO,CACrD,GAAI,EAAE,GACN,KAAM,EAAE,KACT,EAAE,GAKP,MAAM,EAAsB,WAIf,EACX,EAAoB,6BAA+B,IAAI,EAEzD,EAAoB,4BAA8B"}
@@ -15,11 +15,11 @@ declare function getUserNotifications(): Promise<{
15
15
  updatedAt: Date | null;
16
16
  }[] | null>;
17
17
  declare function createNotification(data: CreateNotificationPayload): Promise<{
18
- type: string | null;
19
18
  id: string;
20
19
  createdAt: Date;
21
20
  updatedAt: Date | null;
22
21
  userId: string;
22
+ type: string | null;
23
23
  title: string;
24
24
  content: string | null;
25
25
  target: string | null;
@@ -15,11 +15,11 @@ declare function getUserNotifications(): Promise<{
15
15
  updatedAt: Date | null;
16
16
  }[] | null>;
17
17
  declare function createNotification(data: CreateNotificationPayload): Promise<{
18
- type: string | null;
19
18
  id: string;
20
19
  createdAt: Date;
21
20
  updatedAt: Date | null;
22
21
  userId: string;
22
+ type: string | null;
23
23
  title: string;
24
24
  content: string | null;
25
25
  target: string | null;
@@ -1 +1 @@
1
- {"version":3,"file":"actions.mjs","names":[],"sources":["../../../src/core/notifications/actions.ts"],"sourcesContent":["\"use server\";\n\nimport { desc, eq } from \"drizzle-orm\";\nimport { db } from \"../../server/database/inject.js\";\nimport { notificationTable } from \"../../server/database/schema.js\";\nimport { getCurrentSession } from \"../auth/session.js\";\nimport type { CreateNotificationPayload } from \"./types.js\";\n\nexport async function getUserNotifications() {\n const { user } = await getCurrentSession();\n if (!user) return null;\n\n return await db\n .select()\n .from(notificationTable)\n .where(eq(notificationTable.userId, user.id))\n .orderBy(desc(notificationTable.createdAt));\n}\n\nexport async function createNotification(data: CreateNotificationPayload) {\n const [notification] = await db\n .insert(notificationTable)\n .values({\n ...data,\n isRead: false,\n })\n .returning();\n\n return notification;\n}\n\nexport async function markNotificationAsRead(id: string) {\n const [updated] = await db\n .update(notificationTable)\n .set({ isRead: true })\n .where(eq(notificationTable.id, id))\n .returning();\n\n return updated;\n}\n\nexport async function markAllNotificationsAsRead() {\n const { user } = await getCurrentSession();\n if (!user) return null;\n\n return await db\n .update(notificationTable)\n .set({ isRead: true })\n .where(eq(notificationTable.userId, user.id))\n .returning();\n}\n"],"mappings":"2OAQA,eAAsB,GAAuB,CAC3C,GAAM,CAAE,QAAS,MAAM,GAAmB,CAG1C,OAFK,EAEE,MAAM,EACV,QAAQ,CACR,KAAK,EAAkB,CACvB,MAAM,EAAG,EAAkB,OAAQ,EAAK,GAAG,CAAC,CAC5C,QAAQ,EAAK,EAAkB,UAAU,CAAC,CAN3B,KASpB,eAAsB,EAAmB,EAAiC,CACxE,GAAM,CAAC,GAAgB,MAAM,EAC1B,OAAO,EAAkB,CACzB,OAAO,CACN,GAAG,EACH,OAAQ,GACT,CAAC,CACD,WAAW,CAEd,OAAO,EAGT,eAAsB,EAAuB,EAAY,CACvD,GAAM,CAAC,GAAW,MAAM,EACrB,OAAO,EAAkB,CACzB,IAAI,CAAE,OAAQ,GAAM,CAAC,CACrB,MAAM,EAAG,EAAkB,GAAI,EAAG,CAAC,CACnC,WAAW,CAEd,OAAO,EAGT,eAAsB,GAA6B,CACjD,GAAM,CAAE,QAAS,MAAM,GAAmB,CAG1C,OAFK,EAEE,MAAM,EACV,OAAO,EAAkB,CACzB,IAAI,CAAE,OAAQ,GAAM,CAAC,CACrB,MAAM,EAAG,EAAkB,OAAQ,EAAK,GAAG,CAAC,CAC5C,WAAW,CANI"}
1
+ {"version":3,"file":"actions.mjs","names":[],"sources":["../../../src/core/notifications/actions.ts"],"sourcesContent":["\"use server\";\n\nimport { desc, eq } from \"drizzle-orm\";\nimport { db } from \"../../server/database/inject\";\nimport { notificationTable } from \"../../server/database/schema\";\nimport { getCurrentSession } from \"../auth/session\";\nimport type { CreateNotificationPayload } from \"./types\";\n\nexport async function getUserNotifications() {\n const { user } = await getCurrentSession();\n if (!user) return null;\n\n return await db\n .select()\n .from(notificationTable)\n .where(eq(notificationTable.userId, user.id))\n .orderBy(desc(notificationTable.createdAt));\n}\n\nexport async function createNotification(data: CreateNotificationPayload) {\n const [notification] = await db\n .insert(notificationTable)\n .values({\n ...data,\n isRead: false,\n })\n .returning();\n\n return notification;\n}\n\nexport async function markNotificationAsRead(id: string) {\n const [updated] = await db\n .update(notificationTable)\n .set({ isRead: true })\n .where(eq(notificationTable.id, id))\n .returning();\n\n return updated;\n}\n\nexport async function markAllNotificationsAsRead() {\n const { user } = await getCurrentSession();\n if (!user) return null;\n\n return await db\n .update(notificationTable)\n .set({ isRead: true })\n .where(eq(notificationTable.userId, user.id))\n .returning();\n}\n"],"mappings":"2OAQA,eAAsB,GAAuB,CAC3C,GAAM,CAAE,QAAS,MAAM,GAAmB,CAG1C,OAFK,EAEE,MAAM,EACV,QAAQ,CACR,KAAK,EAAkB,CACvB,MAAM,EAAG,EAAkB,OAAQ,EAAK,GAAG,CAAC,CAC5C,QAAQ,EAAK,EAAkB,UAAU,CAAC,CAN3B,KASpB,eAAsB,EAAmB,EAAiC,CACxE,GAAM,CAAC,GAAgB,MAAM,EAC1B,OAAO,EAAkB,CACzB,OAAO,CACN,GAAG,EACH,OAAQ,GACT,CAAC,CACD,WAAW,CAEd,OAAO,EAGT,eAAsB,EAAuB,EAAY,CACvD,GAAM,CAAC,GAAW,MAAM,EACrB,OAAO,EAAkB,CACzB,IAAI,CAAE,OAAQ,GAAM,CAAC,CACrB,MAAM,EAAG,EAAkB,GAAI,EAAG,CAAC,CACnC,WAAW,CAEd,OAAO,EAGT,eAAsB,GAA6B,CACjD,GAAM,CAAE,QAAS,MAAM,GAAmB,CAG1C,OAFK,EAEE,MAAM,EACV,OAAO,EAAkB,CACzB,IAAI,CAAE,OAAQ,GAAM,CAAC,CACrB,MAAM,EAAG,EAAkB,OAAQ,EAAK,GAAG,CAAC,CAC5C,WAAW,CANI"}
@@ -1 +1 @@
1
- {"version":3,"file":"service.mjs","names":[],"sources":["../../../src/core/notifications/service.ts"],"sourcesContent":["import { eventBus } from \"../event-bus.js\";\nimport { createNotification } from \"./actions.js\";\nimport type { CreateNotificationPayload } from \"./types.js\";\n\nclass NotificationService {\n private initialized = false;\n\n public init() {\n if (this.initialized) {\n // console.log(\"[Notification Service] Already initialized.\");\n return;\n }\n\n console.log(\"[Notification Service] Subscribing to notification:send...\");\n\n eventBus.subscribe(\n \"notification:send\",\n \"notification-service\",\n async (event) => {\n console.log(\n \"[Notification Service] Received notification:send event\",\n event.payload,\n );\n try {\n // const { createNotification } = await import(\"./actions\");\n const result = await createNotification(\n event.payload as CreateNotificationPayload,\n );\n console.log(\n \"[Notification Service] Notification created successfully:\",\n result.id,\n );\n } catch (error) {\n console.error(\n \"[Notification Service] Failed to process notification:send\",\n error,\n );\n }\n },\n );\n\n this.initialized = true;\n console.log(\"[NotificationService] Initialized and listening for events.\");\n }\n}\n\n// Global Singleton Pattern\nconst globalForNotifications = globalThis as unknown as {\n __KRYO_NOTIFICATION_SERVICE__: NotificationService | undefined;\n};\n\nexport const notificationService =\n globalForNotifications.__KRYO_NOTIFICATION_SERVICE__ ??\n new NotificationService();\n\nglobalForNotifications.__KRYO_NOTIFICATION_SERVICE__ = notificationService;\n"],"mappings":"+FAIA,IAAM,EAAN,KAA0B,gCACF,GAEtB,MAAc,CACR,KAAK,cAKT,QAAQ,IAAI,6DAA6D,CAEzE,EAAS,UACP,oBACA,uBACA,KAAO,IAAU,CACf,QAAQ,IACN,0DACA,EAAM,QACP,CACD,GAAI,CAEF,IAAM,EAAS,MAAM,EACnB,EAAM,QACP,CACD,QAAQ,IACN,4DACA,EAAO,GACR,OACM,EAAO,CACd,QAAQ,MACN,6DACA,EACD,GAGN,CAED,KAAK,YAAc,GACnB,QAAQ,IAAI,8DAA8D,IAK9E,MAAM,EAAyB,WAIlB,EACX,EAAuB,+BACvB,IAAI,EAEN,EAAuB,8BAAgC"}
1
+ {"version":3,"file":"service.mjs","names":[],"sources":["../../../src/core/notifications/service.ts"],"sourcesContent":["import { eventBus } from \"../event-bus\";\nimport { createNotification } from \"./actions\";\nimport type { CreateNotificationPayload } from \"./types\";\n\nclass NotificationService {\n private initialized = false;\n\n public init() {\n if (this.initialized) {\n // console.log(\"[Notification Service] Already initialized.\");\n return;\n }\n\n console.log(\"[Notification Service] Subscribing to notification:send...\");\n\n eventBus.subscribe(\n \"notification:send\",\n \"notification-service\",\n async (event) => {\n console.log(\n \"[Notification Service] Received notification:send event\",\n event.payload,\n );\n try {\n // const { createNotification } = await import(\"./actions\");\n const result = await createNotification(\n event.payload as CreateNotificationPayload,\n );\n console.log(\n \"[Notification Service] Notification created successfully:\",\n result.id,\n );\n } catch (error) {\n console.error(\n \"[Notification Service] Failed to process notification:send\",\n error,\n );\n }\n },\n );\n\n this.initialized = true;\n console.log(\"[NotificationService] Initialized and listening for events.\");\n }\n}\n\n// Global Singleton Pattern\nconst globalForNotifications = globalThis as unknown as {\n __KRYO_NOTIFICATION_SERVICE__: NotificationService | undefined;\n};\n\nexport const notificationService =\n globalForNotifications.__KRYO_NOTIFICATION_SERVICE__ ??\n new NotificationService();\n\nglobalForNotifications.__KRYO_NOTIFICATION_SERVICE__ = notificationService;\n"],"mappings":"+FAIA,IAAM,EAAN,KAA0B,gCACF,GAEtB,MAAc,CACR,KAAK,cAKT,QAAQ,IAAI,6DAA6D,CAEzE,EAAS,UACP,oBACA,uBACA,KAAO,IAAU,CACf,QAAQ,IACN,0DACA,EAAM,QACP,CACD,GAAI,CAEF,IAAM,EAAS,MAAM,EACnB,EAAM,QACP,CACD,QAAQ,IACN,4DACA,EAAO,GACR,OACM,EAAO,CACd,QAAQ,MACN,6DACA,EACD,GAGN,CAED,KAAK,YAAc,GACnB,QAAQ,IAAI,8DAA8D,IAK9E,MAAM,EAAyB,WAIlB,EACX,EAAuB,+BACvB,IAAI,EAEN,EAAuB,8BAAgC"}
@@ -1 +1 @@
1
- {"version":3,"file":"setup.mjs","names":[],"sources":["../../src/core/setup.ts"],"sourcesContent":["import { sql } from \"drizzle-orm\";\nimport { db } from \"../server/database/inject.js\";\nimport { userTable } from \"../server/database/schema.js\";\n\n/**\n * Checks if the system is installed.\n * A system is considered installed if at least one user exists in the database.\n */\nexport async function isSystemInstalled(): Promise<boolean> {\n try {\n // We check if the users table exists and has at least one record\n console.log(\"[Kernel:Setup] Checking if system is installed...\");\n const result = await db\n .select({ count: sql<string | number>`count(*)` })\n .from(userTable);\n const count = Number(result[0]?.count || 0);\n console.log(`[Kernel:Setup] User count: ${count}`);\n return count > 0;\n } catch (error: any) {\n console.log(\n `[Kernel:Setup] System not installed or error: ${error.message}`,\n );\n // If table doesn't exist, it's definitely not installed\n return false;\n }\n}\n"],"mappings":"+IAQA,eAAsB,GAAsC,CAC1D,GAAI,CAEF,QAAQ,IAAI,oDAAoD,CAChE,IAAM,EAAS,MAAM,EAClB,OAAO,CAAE,MAAO,CAAoB,WAAY,CAAC,CACjD,KAAK,EAAU,CACZ,EAAQ,OAAO,EAAO,IAAI,OAAS,EAAE,CAE3C,OADA,QAAQ,IAAI,8BAA8B,IAAQ,CAC3C,EAAQ,QACR,EAAY,CAKnB,OAJA,QAAQ,IACN,iDAAiD,EAAM,UACxD,CAEM"}
1
+ {"version":3,"file":"setup.mjs","names":[],"sources":["../../src/core/setup.ts"],"sourcesContent":["import { sql } from \"drizzle-orm\";\nimport { db } from \"../server/database/inject\";\nimport { userTable } from \"../server/database/schema\";\n\n/**\n * Checks if the system is installed.\n * A system is considered installed if at least one user exists in the database.\n */\nexport async function isSystemInstalled(): Promise<boolean> {\n try {\n // We check if the users table exists and has at least one record\n console.log(\"[Kernel:Setup] Checking if system is installed...\");\n const result = await db\n .select({ count: sql<string | number>`count(*)` })\n .from(userTable);\n const count = Number(result[0]?.count || 0);\n console.log(`[Kernel:Setup] User count: ${count}`);\n return count > 0;\n } catch (error: any) {\n console.log(\n `[Kernel:Setup] System not installed or error: ${error.message}`,\n );\n // If table doesn't exist, it's definitely not installed\n return false;\n }\n}\n"],"mappings":"+IAQA,eAAsB,GAAsC,CAC1D,GAAI,CAEF,QAAQ,IAAI,oDAAoD,CAChE,IAAM,EAAS,MAAM,EAClB,OAAO,CAAE,MAAO,CAAoB,WAAY,CAAC,CACjD,KAAK,EAAU,CACZ,EAAQ,OAAO,EAAO,IAAI,OAAS,EAAE,CAE3C,OADA,QAAQ,IAAI,8BAA8B,IAAQ,CAC3C,EAAQ,QACR,EAAY,CAKnB,OAJA,QAAQ,IACN,iDAAiD,EAAM,UACxD,CAEM"}
@@ -1 +1 @@
1
- {"version":3,"file":"email.mjs","names":[],"sources":["../../../src/server/auth/email.ts"],"sourcesContent":["import { count, eq } from \"drizzle-orm\";\nimport { db } from \"../database/inject.js\";\nimport { userTable } from \"../database/schema.js\";\n\n/**\n * Validates the email format and length.\n */\nexport function verifyEmailInput(email: string): boolean {\n return /^.+@.+\\..+$/.test(email) && email.length < 256 && email.length > 0;\n}\n\n/**\n * Checks if an email address is already in use.\n * @returns True if the email is available, false otherwise.\n */\nexport async function checkEmailAvailability(email: string): Promise<boolean> {\n const [entries] = await db\n .select({ count: count() })\n .from(userTable)\n .where(eq(userTable.email, email));\n\n return entries.count === 0;\n}\n"],"mappings":"2IAOA,SAAgB,EAAiB,EAAwB,CACvD,MAAO,cAAc,KAAK,EAAM,EAAI,EAAM,OAAS,KAAO,EAAM,OAAS,EAO3E,eAAsB,EAAuB,EAAiC,CAC5E,GAAM,CAAC,GAAW,MAAM,EACrB,OAAO,CAAE,MAAO,GAAO,CAAE,CAAC,CAC1B,KAAK,EAAU,CACf,MAAM,EAAG,EAAU,MAAO,EAAM,CAAC,CAEpC,OAAO,EAAQ,QAAU"}
1
+ {"version":3,"file":"email.mjs","names":[],"sources":["../../../src/server/auth/email.ts"],"sourcesContent":["import { count, eq } from \"drizzle-orm\";\nimport { db } from \"../database/inject\";\nimport { userTable } from \"../database/schema\";\n\n/**\n * Validates the email format and length.\n */\nexport function verifyEmailInput(email: string): boolean {\n return /^.+@.+\\..+$/.test(email) && email.length < 256 && email.length > 0;\n}\n\n/**\n * Checks if an email address is already in use.\n * @returns True if the email is available, false otherwise.\n */\nexport async function checkEmailAvailability(email: string): Promise<boolean> {\n const [entries] = await db\n .select({ count: count() })\n .from(userTable)\n .where(eq(userTable.email, email));\n\n return entries.count === 0;\n}\n"],"mappings":"2IAOA,SAAgB,EAAiB,EAAwB,CACvD,MAAO,cAAc,KAAK,EAAM,EAAI,EAAM,OAAS,KAAO,EAAM,OAAS,EAO3E,eAAsB,EAAuB,EAAiC,CAC5E,GAAM,CAAC,GAAW,MAAM,EACrB,OAAO,CAAE,MAAO,GAAO,CAAE,CAAC,CAC1B,KAAK,EAAU,CACf,MAAM,EAAG,EAAU,MAAO,EAAM,CAAC,CAEpC,OAAO,EAAQ,QAAU"}
@@ -1 +1 @@
1
- {"version":3,"file":"user.mjs","names":[],"sources":["../../../src/server/auth/user.ts"],"sourcesContent":["\"use server\";\n\nimport { and, eq } from \"drizzle-orm\";\nimport type { User } from \"../../core/auth/types.js\";\nimport { generateRandomRecoveryCode } from \"../../core/auth/utils/encode.js\";\nimport {\n decryptToString,\n encryptString,\n} from \"../../core/auth/utils/encryption.js\";\nimport { db } from \"../database/inject.js\";\nimport { rolesTable, usersToRolesTable, userTable } from \"../database/schema.js\";\nimport { sendRecoveryCode } from \"../emails/index.js\";\nimport { hashPassword } from \"./password.js\";\n\n/**\n * Validates the username input.\n */\nexport async function verifyUsernameInput(username: string): Promise<boolean> {\n return (\n username.length > 3 && username.length < 32 && username.trim() === username\n );\n}\n\n/**\n * Creates a new user with an initial recovery code and default 'user' role.\n */\nexport async function createUser(\n email: string,\n username: string,\n password: string,\n): Promise<User> {\n const passwordHash = await hashPassword(password);\n const recoveryCode = generateRandomRecoveryCode();\n const encryptedRecoveryCode = encryptString(recoveryCode);\n\n return await db.transaction(async (tx) => {\n const [row] = await tx\n .insert(userTable)\n .values({\n email: email,\n name: username,\n password: passwordHash,\n recovery_code: Buffer.from(encryptedRecoveryCode),\n })\n .returning();\n\n if (!row) {\n throw new Error(\"Failed to create user\");\n }\n\n // Assign default 'user' role\n let [role] = await tx\n .select()\n .from(rolesTable)\n .where(eq(rolesTable.name, \"user\"));\n\n if (!role) {\n [role] = await tx\n .insert(rolesTable)\n .values({ name: \"user\", description: \"Default user role\" })\n .returning();\n }\n\n await tx.insert(usersToRolesTable).values({\n userId: row.id,\n roleId: role.id,\n });\n\n await sendRecoveryCode(row.email, recoveryCode);\n\n return row;\n });\n}\n\n/**\n * Creates a new user from an OAuth provider.\n */\nexport async function createOAuthUser(\n email: string,\n name: string,\n image?: string,\n): Promise<User> {\n const recoveryCode = generateRandomRecoveryCode();\n const encryptedRecoveryCode = encryptString(recoveryCode);\n\n return await db.transaction(async (tx) => {\n const [row] = await tx\n .insert(userTable)\n .values({\n email: email,\n name: name,\n image: image,\n emailVerifiedAt: new Date(),\n recovery_code: Buffer.from(encryptedRecoveryCode),\n })\n .returning();\n\n // Assign default 'user' role\n let [role] = await tx\n .select()\n .from(rolesTable)\n .where(eq(rolesTable.name, \"user\"));\n\n if (!role) {\n [role] = await tx\n .insert(rolesTable)\n .values({ name: \"user\", description: \"Default user role\" })\n .returning();\n }\n\n await tx.insert(usersToRolesTable).values({\n userId: row.id,\n roleId: role.id,\n });\n\n return row;\n });\n}\n\n/**\n * Returns a user by ID.\n */\nexport async function getUserById(userId: string): Promise<User | null> {\n const [user] = await db\n .select()\n .from(userTable)\n .where(eq(userTable.id, userId));\n\n if (!user) return null;\n const { password, recovery_code, ...safeUser } = user;\n return safeUser as User;\n}\n\n/**\n * Decrypts and returns the user's recovery code.\n */\nexport async function getUserRecoverCode(userId: string): Promise<string> {\n const [user] = await db\n .select({\n recovery_code: userTable.recovery_code,\n })\n .from(userTable)\n .where(eq(userTable.id, userId));\n\n if (!user || !user.recovery_code) {\n throw new Error(\"Recovery code not found for user\");\n }\n\n return decryptToString(user.recovery_code);\n}\n\n/**\n * Generates and sets a new recovery code for the user.\n */\nexport async function resetUserRecoveryCode(userId: string): Promise<string> {\n const recoveryCode = generateRandomRecoveryCode();\n const encrypted = encryptString(recoveryCode);\n const [currentUser] = await db\n .update(userTable)\n .set({\n recovery_code: Buffer.from(encrypted),\n })\n .where(eq(userTable.id, userId))\n .returning();\n\n if (!currentUser) {\n throw new Error(\"User not found\");\n }\n\n await sendRecoveryCode(currentUser.email, recoveryCode);\n\n return recoveryCode;\n}\n\n/**\n * Updates the user's password.\n */\nexport async function updateUserPassword(\n userId: string,\n password: string,\n): Promise<void> {\n const passwordHash = await hashPassword(password);\n\n await db\n .update(userTable)\n .set({\n password: passwordHash,\n })\n .where(eq(userTable.id, userId));\n}\n\n/**\n * Updates the user's name.\n */\nexport async function updateUserName(\n userId: string,\n name: string,\n): Promise<void> {\n await db\n .update(userTable)\n .set({\n name: name,\n })\n .where(eq(userTable.id, userId));\n}\n/**\n * Updates the user's image.\n */\nexport async function updateUserAwatar(\n userId: string,\n image: string,\n): Promise<void> {\n await db\n .update(userTable)\n .set({\n image,\n })\n .where(eq(userTable.id, userId));\n}\n\n/**\n * Updates the user's email and marks it as verified.\n */\nexport async function updateUserEmailAndSetEmailAsVerified(\n userId: string,\n email: string,\n): Promise<void> {\n await db\n .update(userTable)\n .set({\n email: email,\n emailVerifiedAt: new Date(),\n })\n .where(eq(userTable.id, userId));\n}\n\n/**\n * Sets the user as email verified if the provided email matches.\n */\nexport async function setUserAsEmailVerifiedIfEmailMatches(\n userId: string,\n email: string,\n): Promise<boolean> {\n const result = await db\n .update(userTable)\n .set({\n emailVerifiedAt: new Date(),\n })\n .where(and(eq(userTable.id, userId), eq(userTable.email, email)))\n .returning({ id: userTable.id });\n\n return result.length > 0;\n}\n\n/**\n * Returns the user's password hash.\n */\nexport async function getUserPasswordHash(\n userId: string,\n): Promise<string | null> {\n const [user] = await db\n .select({\n password: userTable.password,\n })\n .from(userTable)\n .where(eq(userTable.id, userId));\n\n if (!user) {\n throw new Error(\"User not found\");\n }\n\n return user.password;\n}\n\n/**\n * Returns a user by email.\n */\nexport async function getUserFromEmail(email: string): Promise<User | null> {\n const [user] = await db\n .select()\n .from(userTable)\n .where(eq(userTable.email, email));\n\n if (!user) return null;\n const { password, recovery_code, ...safeUser } = user;\n return safeUser as User;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"user.mjs","names":[],"sources":["../../../src/server/auth/user.ts"],"sourcesContent":["\"use server\";\n\nimport { and, eq } from \"drizzle-orm\";\nimport type { User } from \"../../core/auth/types\";\nimport { generateRandomRecoveryCode } from \"../../core/auth/utils/encode\";\nimport {\n decryptToString,\n encryptString,\n} from \"../../core/auth/utils/encryption\";\nimport { db } from \"../database/inject\";\nimport { rolesTable, usersToRolesTable, userTable } from \"../database/schema\";\nimport { sendRecoveryCode } from \"../emails/index\";\nimport { hashPassword } from \"./password\";\n\n/**\n * Validates the username input.\n */\nexport async function verifyUsernameInput(username: string): Promise<boolean> {\n return (\n username.length > 3 && username.length < 32 && username.trim() === username\n );\n}\n\n/**\n * Creates a new user with an initial recovery code and default 'user' role.\n */\nexport async function createUser(\n email: string,\n username: string,\n password: string,\n): Promise<User> {\n const passwordHash = await hashPassword(password);\n const recoveryCode = generateRandomRecoveryCode();\n const encryptedRecoveryCode = encryptString(recoveryCode);\n\n return await db.transaction(async (tx) => {\n const [row] = await tx\n .insert(userTable)\n .values({\n email: email,\n name: username,\n password: passwordHash,\n recovery_code: Buffer.from(encryptedRecoveryCode),\n })\n .returning();\n\n if (!row) {\n throw new Error(\"Failed to create user\");\n }\n\n // Assign default 'user' role\n let [role] = await tx\n .select()\n .from(rolesTable)\n .where(eq(rolesTable.name, \"user\"));\n\n if (!role) {\n [role] = await tx\n .insert(rolesTable)\n .values({ name: \"user\", description: \"Default user role\" })\n .returning();\n }\n\n await tx.insert(usersToRolesTable).values({\n userId: row.id,\n roleId: role.id,\n });\n\n await sendRecoveryCode(row.email, recoveryCode);\n\n return row;\n });\n}\n\n/**\n * Creates a new user from an OAuth provider.\n */\nexport async function createOAuthUser(\n email: string,\n name: string,\n image?: string,\n): Promise<User> {\n const recoveryCode = generateRandomRecoveryCode();\n const encryptedRecoveryCode = encryptString(recoveryCode);\n\n return await db.transaction(async (tx) => {\n const [row] = await tx\n .insert(userTable)\n .values({\n email: email,\n name: name,\n image: image,\n emailVerifiedAt: new Date(),\n recovery_code: Buffer.from(encryptedRecoveryCode),\n })\n .returning();\n\n // Assign default 'user' role\n let [role] = await tx\n .select()\n .from(rolesTable)\n .where(eq(rolesTable.name, \"user\"));\n\n if (!role) {\n [role] = await tx\n .insert(rolesTable)\n .values({ name: \"user\", description: \"Default user role\" })\n .returning();\n }\n\n await tx.insert(usersToRolesTable).values({\n userId: row.id,\n roleId: role.id,\n });\n\n return row;\n });\n}\n\n/**\n * Returns a user by ID.\n */\nexport async function getUserById(userId: string): Promise<User | null> {\n const [user] = await db\n .select()\n .from(userTable)\n .where(eq(userTable.id, userId));\n\n if (!user) return null;\n const { password, recovery_code, ...safeUser } = user;\n return safeUser as User;\n}\n\n/**\n * Decrypts and returns the user's recovery code.\n */\nexport async function getUserRecoverCode(userId: string): Promise<string> {\n const [user] = await db\n .select({\n recovery_code: userTable.recovery_code,\n })\n .from(userTable)\n .where(eq(userTable.id, userId));\n\n if (!user || !user.recovery_code) {\n throw new Error(\"Recovery code not found for user\");\n }\n\n return decryptToString(user.recovery_code);\n}\n\n/**\n * Generates and sets a new recovery code for the user.\n */\nexport async function resetUserRecoveryCode(userId: string): Promise<string> {\n const recoveryCode = generateRandomRecoveryCode();\n const encrypted = encryptString(recoveryCode);\n const [currentUser] = await db\n .update(userTable)\n .set({\n recovery_code: Buffer.from(encrypted),\n })\n .where(eq(userTable.id, userId))\n .returning();\n\n if (!currentUser) {\n throw new Error(\"User not found\");\n }\n\n await sendRecoveryCode(currentUser.email, recoveryCode);\n\n return recoveryCode;\n}\n\n/**\n * Updates the user's password.\n */\nexport async function updateUserPassword(\n userId: string,\n password: string,\n): Promise<void> {\n const passwordHash = await hashPassword(password);\n\n await db\n .update(userTable)\n .set({\n password: passwordHash,\n })\n .where(eq(userTable.id, userId));\n}\n\n/**\n * Updates the user's name.\n */\nexport async function updateUserName(\n userId: string,\n name: string,\n): Promise<void> {\n await db\n .update(userTable)\n .set({\n name: name,\n })\n .where(eq(userTable.id, userId));\n}\n/**\n * Updates the user's image.\n */\nexport async function updateUserAwatar(\n userId: string,\n image: string,\n): Promise<void> {\n await db\n .update(userTable)\n .set({\n image,\n })\n .where(eq(userTable.id, userId));\n}\n\n/**\n * Updates the user's email and marks it as verified.\n */\nexport async function updateUserEmailAndSetEmailAsVerified(\n userId: string,\n email: string,\n): Promise<void> {\n await db\n .update(userTable)\n .set({\n email: email,\n emailVerifiedAt: new Date(),\n })\n .where(eq(userTable.id, userId));\n}\n\n/**\n * Sets the user as email verified if the provided email matches.\n */\nexport async function setUserAsEmailVerifiedIfEmailMatches(\n userId: string,\n email: string,\n): Promise<boolean> {\n const result = await db\n .update(userTable)\n .set({\n emailVerifiedAt: new Date(),\n })\n .where(and(eq(userTable.id, userId), eq(userTable.email, email)))\n .returning({ id: userTable.id });\n\n return result.length > 0;\n}\n\n/**\n * Returns the user's password hash.\n */\nexport async function getUserPasswordHash(\n userId: string,\n): Promise<string | null> {\n const [user] = await db\n .select({\n password: userTable.password,\n })\n .from(userTable)\n .where(eq(userTable.id, userId));\n\n if (!user) {\n throw new Error(\"User not found\");\n }\n\n return user.password;\n}\n\n/**\n * Returns a user by email.\n */\nexport async function getUserFromEmail(email: string): Promise<User | null> {\n const [user] = await db\n .select()\n .from(userTable)\n .where(eq(userTable.email, email));\n\n if (!user) return null;\n const { password, recovery_code, ...safeUser } = user;\n return safeUser as User;\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"inject.mjs","names":[],"sources":["../../../src/server/database/inject.ts"],"sourcesContent":["import type { NodePgDatabase } from \"drizzle-orm/node-postgres\";\nimport type * as schema from \"./schema.js\";\n\nexport type KryoDatabase = NodePgDatabase<typeof schema>;\n\n// Use globalThis to persist the DB across multiple bundles/instances\nconst globalForDb = globalThis as any;\n\nexport function injectDb(db: KryoDatabase) {\n if (globalForDb.__KRYO_DB__) return;\n console.log(\"[Kryo:Core] >>> DATABASE INJECTED <<<\");\n globalForDb.__KRYO_DB__ = db;\n}\n\n/**\n * Shared 'db' proxy.\n * Resolves to globalThis.__KRYO_DB__ on every property access.\n * Safe to import statically at top-level.\n */\nexport const db = new Proxy({} as KryoDatabase, {\n get(_, prop) {\n if (prop === \"then\") return undefined;\n\n // Internal Drizzle/Node/Debug checks\n if (typeof prop === \"symbol\" || prop === \"inspect\" || prop === \"toString\") {\n return (globalForDb.__KRYO_DB__ as any)?.[prop];\n }\n\n const database = globalForDb.__KRYO_DB__;\n\n if (!database) {\n throw new Error(\n `[Kryo:Core] Database access error: tried to use \"db.${String(prop)}\" but database is not injected yet. Ensure you call \"ensureSystemInitialized()\" before using the database.`,\n );\n }\n\n return (database as any)[prop];\n },\n});\n"],"mappings":"AAMA,MAAM,EAAc,WAEpB,SAAgB,EAAS,EAAkB,CACrC,AAEJ,EAAY,eADZ,QAAQ,IAAI,wCAAwC,CAC1B,GAQ5B,MAAa,EAAK,IAAI,MAAM,EAAE,CAAkB,CAC9C,IAAI,EAAG,EAAM,CACX,GAAI,IAAS,OAAQ,OAGrB,GAAI,OAAO,GAAS,UAAY,IAAS,WAAa,IAAS,WAC7D,OAAQ,EAAY,cAAsB,GAG5C,IAAM,EAAW,EAAY,YAE7B,GAAI,CAAC,EACH,MAAU,MACR,uDAAuD,OAAO,EAAK,CAAC,4GACrE,CAGH,OAAQ,EAAiB,IAE5B,CAAC"}
1
+ {"version":3,"file":"inject.mjs","names":[],"sources":["../../../src/server/database/inject.ts"],"sourcesContent":["import type { NodePgDatabase } from \"drizzle-orm/node-postgres\";\nimport type * as schema from \"./schema\";\n\nexport type KryoDatabase = NodePgDatabase<typeof schema>;\n\n// Use globalThis to persist the DB across multiple bundles/instances\nconst globalForDb = globalThis as any;\n\nexport function injectDb(db: KryoDatabase) {\n if (globalForDb.__KRYO_DB__) return;\n console.log(\"[Kryo:Core] >>> DATABASE INJECTED <<<\");\n globalForDb.__KRYO_DB__ = db;\n}\n\n/**\n * Shared 'db' proxy.\n * Resolves to globalThis.__KRYO_DB__ on every property access.\n * Safe to import statically at top-level.\n */\nexport const db = new Proxy({} as KryoDatabase, {\n get(_, prop) {\n if (prop === \"then\") return undefined;\n\n // Internal Drizzle/Node/Debug checks\n if (typeof prop === \"symbol\" || prop === \"inspect\" || prop === \"toString\") {\n return (globalForDb.__KRYO_DB__ as any)?.[prop];\n }\n\n const database = globalForDb.__KRYO_DB__;\n\n if (!database) {\n throw new Error(\n `[Kryo:Core] Database access error: tried to use \"db.${String(prop)}\" but database is not injected yet. Ensure you call \"ensureSystemInitialized()\" before using the database.`,\n );\n }\n\n return (database as any)[prop];\n },\n});\n"],"mappings":"AAMA,MAAM,EAAc,WAEpB,SAAgB,EAAS,EAAkB,CACrC,AAEJ,EAAY,eADZ,QAAQ,IAAI,wCAAwC,CAC1B,GAQ5B,MAAa,EAAK,IAAI,MAAM,EAAE,CAAkB,CAC9C,IAAI,EAAG,EAAM,CACX,GAAI,IAAS,OAAQ,OAGrB,GAAI,OAAO,GAAS,UAAY,IAAS,WAAa,IAAS,WAC7D,OAAQ,EAAY,cAAsB,GAG5C,IAAM,EAAW,EAAY,YAE7B,GAAI,CAAC,EACH,MAAU,MACR,uDAAuD,OAAO,EAAK,CAAC,4GACrE,CAGH,OAAQ,EAAiB,IAE5B,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@arch-cadre/core",
3
- "version": "0.0.26",
3
+ "version": "0.0.27",
4
4
  "type": "module",
5
5
  "description": "Core logic for Kryo framework",
6
6
  "exports": {