@fragno-dev/auth 0.0.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.md +16 -0
- package/README.md +16 -0
- package/dist/browser/client/react.d.ts +122 -0
- package/dist/browser/client/react.d.ts.map +1 -0
- package/dist/browser/client/react.js +11 -0
- package/dist/browser/client/react.js.map +1 -0
- package/dist/browser/client/solid.d.ts +122 -0
- package/dist/browser/client/solid.d.ts.map +1 -0
- package/dist/browser/client/solid.js +11 -0
- package/dist/browser/client/solid.js.map +1 -0
- package/dist/browser/client/svelte.d.ts +122 -0
- package/dist/browser/client/svelte.d.ts.map +1 -0
- package/dist/browser/client/svelte.js +11 -0
- package/dist/browser/client/svelte.js.map +1 -0
- package/dist/browser/client/vanilla.d.ts +122 -0
- package/dist/browser/client/vanilla.d.ts.map +1 -0
- package/dist/browser/client/vanilla.js +11 -0
- package/dist/browser/client/vanilla.js.map +1 -0
- package/dist/browser/client/vue.d.ts +122 -0
- package/dist/browser/client/vue.d.ts.map +1 -0
- package/dist/browser/client/vue.js +11 -0
- package/dist/browser/client/vue.js.map +1 -0
- package/dist/browser/index.d.ts +600 -0
- package/dist/browser/index.d.ts.map +1 -0
- package/dist/browser/index.js +3 -0
- package/dist/browser/src-DNrh9CQq.js +184 -0
- package/dist/browser/src-DNrh9CQq.js.map +1 -0
- package/dist/node/index.d.ts +600 -0
- package/dist/node/index.d.ts.map +1 -0
- package/dist/node/index.js +677 -0
- package/dist/node/index.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/package.json +97 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","names":["password: string","storedHash: string","value: string","cookieHeader: string | null","cookies: Record<string, string>","value: string","options: CookieOptions","headers: Headers","queryParam?: string | null","bodySessionId?: string","email: string","passwordHash: string","role: \"user\" | \"admin\"","userId: string","sessionId: string","b","cookieOptions?: CookieOptions","sessionId: string","userId: string","b","headers: Headers","user: {\n id: unknown;\n email: string;\n role: string;\n createdAt: Date;\n }","params: GetUsersParams","effectiveSortBy: SortField","query","cursorParam: string | null","sortBy: SortField","config: AuthConfig","fragnoConfig: FragnoPublicConfigWithDatabase","fragnoConfig?: FragnoPublicClientConfig","params?: { sessionId?: string }"],"sources":["../../src/schema.ts","../../src/user/password.ts","../../src/utils/cookie.ts","../../src/user/user-actions.ts","../../src/session/session.ts","../../src/user/user-overview.ts","../../src/index.ts"],"sourcesContent":["import { column, idColumn, referenceColumn, schema } from \"@fragno-dev/db/schema\";\n\nexport const authSchema = schema(\"auth\", (s) => {\n return s\n .addTable(\"user\", (t) => {\n return t\n .addColumn(\"id\", idColumn())\n .addColumn(\"email\", column(\"string\"))\n .addColumn(\"passwordHash\", column(\"string\"))\n .addColumn(\"role\", column(\"string\").defaultTo(\"user\"))\n .addColumn(\n \"createdAt\",\n column(\"timestamp\").defaultTo((b) => b.now()),\n )\n .createIndex(\"idx_user_email\", [\"email\"])\n .createIndex(\"idx_user_id\", [\"id\"], { unique: true });\n })\n .addTable(\"session\", (t) => {\n return t\n .addColumn(\"id\", idColumn())\n .addColumn(\"userId\", referenceColumn())\n .addColumn(\"expiresAt\", column(\"timestamp\"))\n .addColumn(\n \"createdAt\",\n column(\"timestamp\").defaultTo((b) => b.now()),\n )\n .createIndex(\"idx_session_user\", [\"userId\"]);\n })\n .addReference(\"sessionOwner\", {\n from: {\n table: \"session\",\n column: \"userId\",\n },\n to: {\n table: \"user\",\n column: \"id\",\n },\n type: \"one\",\n })\n .alterTable(\"user\", (t) => {\n return t.createIndex(\"idx_user_createdAt\", [\"createdAt\"]);\n });\n});\n","// Password hashing utilities using WebCrypto\nexport async function hashPassword(password: string): Promise<string> {\n const encoder = new TextEncoder();\n const salt = crypto.getRandomValues(new Uint8Array(16));\n const iterations = 100000;\n\n const keyMaterial = await crypto.subtle.importKey(\n \"raw\",\n encoder.encode(password),\n \"PBKDF2\",\n false,\n [\"deriveBits\"],\n );\n\n const hashBuffer = await crypto.subtle.deriveBits(\n {\n name: \"PBKDF2\",\n salt: salt,\n iterations: iterations,\n hash: \"SHA-256\",\n },\n keyMaterial,\n 256,\n );\n\n const hashArray = Array.from(new Uint8Array(hashBuffer));\n const saltArray = Array.from(salt);\n\n return `${saltArray.map((b) => b.toString(16).padStart(2, \"0\")).join(\"\")}:${iterations}:${hashArray.map((b) => b.toString(16).padStart(2, \"0\")).join(\"\")}`;\n}\n\nexport async function verifyPassword(password: string, storedHash: string): Promise<boolean> {\n const parts = storedHash.split(\":\");\n if (parts.length !== 3) {\n return false;\n }\n\n const [saltHex, iterationsStr, hashHex] = parts;\n const iterations = Number.parseInt(iterationsStr, 10);\n const isHex = (value: string) => /^[0-9a-f]+$/i.test(value) && value.length % 2 === 0;\n\n if (!saltHex || !hashHex || !isHex(saltHex) || !isHex(hashHex)) {\n return false;\n }\n\n if (!Number.isFinite(iterations) || iterations <= 0) {\n return false;\n }\n\n const saltPairs = saltHex.match(/.{1,2}/g);\n const hashPairs = hashHex.match(/.{1,2}/g);\n\n if (!saltPairs || !hashPairs) {\n return false;\n }\n\n const salt = new Uint8Array(saltPairs.map((byte) => Number.parseInt(byte, 16)));\n const storedHashBytes = new Uint8Array(hashPairs.map((byte) => Number.parseInt(byte, 16)));\n\n const encoder = new TextEncoder();\n const keyMaterial = await crypto.subtle.importKey(\n \"raw\",\n encoder.encode(password),\n \"PBKDF2\",\n false,\n [\"deriveBits\"],\n );\n\n const hashBuffer = await crypto.subtle.deriveBits(\n {\n name: \"PBKDF2\",\n salt: salt,\n iterations: iterations,\n hash: \"SHA-256\",\n },\n keyMaterial,\n 256,\n );\n\n const hashArray = new Uint8Array(hashBuffer);\n\n if (hashArray.length !== storedHashBytes.length) {\n return false;\n }\n\n let isEqual = true;\n for (let i = 0; i < hashArray.length; i++) {\n if (hashArray[i] !== storedHashBytes[i]) {\n isEqual = false;\n }\n }\n return isEqual;\n}\n","/**\n * Cookie utilities for session management\n */\n\nexport const COOKIE_NAME = \"sessionid\";\nconst MAX_AGE = 2592000; // 30 days in seconds\n\nexport interface CookieOptions {\n httpOnly?: boolean;\n secure?: boolean;\n sameSite?: \"Strict\" | \"Lax\" | \"None\";\n maxAge?: number;\n path?: string;\n}\n\n/**\n * Parse cookies from a Cookie header string\n */\nexport function parseCookies(cookieHeader: string | null): Record<string, string> {\n if (!cookieHeader) {\n return {};\n }\n\n const cookies: Record<string, string> = {};\n const pairs = cookieHeader.split(\";\");\n\n for (const pair of pairs) {\n const [key, ...valueParts] = pair.split(\"=\");\n const trimmedKey = key?.trim();\n const value = valueParts.join(\"=\").trim();\n\n if (trimmedKey) {\n cookies[trimmedKey] = decodeURIComponent(value);\n }\n }\n\n return cookies;\n}\n\n/**\n * Build a Set-Cookie header string with security attributes\n */\nexport function buildSetCookieHeader(value: string, options: CookieOptions = {}): string {\n const {\n httpOnly = true,\n secure = true,\n sameSite = \"Strict\",\n maxAge = MAX_AGE,\n path = \"/\",\n } = options;\n const effectiveSecure = sameSite === \"None\" ? true : secure;\n\n const parts = [\n `${COOKIE_NAME}=${encodeURIComponent(value)}`,\n `Max-Age=${maxAge}`,\n `Path=${path}`,\n ];\n\n if (httpOnly) {\n parts.push(\"HttpOnly\");\n }\n\n if (effectiveSecure) {\n parts.push(\"Secure\");\n }\n\n if (sameSite) {\n parts.push(`SameSite=${sameSite}`);\n }\n\n return parts.join(\"; \");\n}\n\n/**\n * Build a Set-Cookie header to clear the session cookie\n */\nexport function buildClearCookieHeader(options: CookieOptions = {}): string {\n return buildSetCookieHeader(\"\", { ...options, maxAge: 0 });\n}\n\n/**\n * Extract session ID from headers, checking cookies first, then query/body\n */\nexport function extractSessionId(\n headers: Headers,\n queryParam?: string | null,\n bodySessionId?: string,\n): string | null {\n // First, try to get from cookies\n const cookieHeader = headers.get(\"Cookie\");\n const cookies = parseCookies(cookieHeader);\n const sessionIdFromCookie = cookies[COOKIE_NAME];\n\n if (sessionIdFromCookie) {\n return sessionIdFromCookie;\n }\n\n // Fall back to query parameter\n if (queryParam) {\n return queryParam;\n }\n\n // Fall back to body\n if (bodySessionId) {\n return bodySessionId;\n }\n\n return null;\n}\n","import { defineRoute, defineRoutes } from \"@fragno-dev/core\";\nimport type { DatabaseServiceContext } from \"@fragno-dev/db\";\nimport { authSchema } from \"../schema\";\nimport { z } from \"zod\";\nimport { hashPassword, verifyPassword } from \"./password\";\nimport { buildSetCookieHeader, extractSessionId } from \"../utils/cookie\";\nimport type { authFragmentDefinition } from \"..\";\n\ntype AuthServiceContext = DatabaseServiceContext<{}>;\n\nexport function createUserServices() {\n return {\n createUser: function (\n this: AuthServiceContext,\n email: string,\n passwordHash: string,\n role: \"user\" | \"admin\" = \"user\",\n ) {\n return this.serviceTx(authSchema)\n .mutate(({ uow }) => {\n const id = uow.create(\"user\", {\n email,\n passwordHash,\n role,\n });\n return {\n id: id.valueOf(),\n email,\n role,\n };\n })\n .build();\n },\n getUserByEmail: function (this: AuthServiceContext, email: string) {\n return this.serviceTx(authSchema)\n .retrieve((uow) =>\n uow.findFirst(\"user\", (b) =>\n b.whereIndex(\"idx_user_email\", (eb) => eb(\"email\", \"=\", email)),\n ),\n )\n .transformRetrieve(([user]) =>\n user\n ? {\n id: user.id.valueOf(),\n email: user.email,\n passwordHash: user.passwordHash,\n role: user.role as \"user\" | \"admin\",\n }\n : null,\n )\n .build();\n },\n updateUserRole: function (this: AuthServiceContext, userId: string, role: \"user\" | \"admin\") {\n return this.serviceTx(authSchema)\n .mutate(({ uow }) => {\n uow.update(\"user\", userId, (b) => b.set({ role }));\n return { success: true };\n })\n .build();\n },\n updateUserPassword: function (this: AuthServiceContext, userId: string, passwordHash: string) {\n return this.serviceTx(authSchema)\n .mutate(({ uow }) => {\n uow.update(\"user\", userId, (b) => b.set({ passwordHash }));\n return { success: true };\n })\n .build();\n },\n signUpWithSession: function (this: AuthServiceContext, email: string, passwordHash: string) {\n const expiresAt = new Date();\n expiresAt.setDate(expiresAt.getDate() + 30); // 30 days from now\n\n return this.serviceTx(authSchema)\n .retrieve((uow) =>\n uow.findFirst(\"user\", (b) =>\n b.whereIndex(\"idx_user_email\", (eb) => eb(\"email\", \"=\", email)),\n ),\n )\n .mutate(({ uow, retrieveResult: [existingUser] }) => {\n if (existingUser) {\n return { ok: false as const, code: \"email_already_exists\" as const };\n }\n\n const userId = uow.create(\"user\", {\n email,\n passwordHash,\n role: \"user\",\n });\n\n const sessionId = uow.create(\"session\", {\n userId,\n expiresAt,\n });\n\n return {\n ok: true as const,\n sessionId: sessionId.valueOf(),\n userId: userId.valueOf(),\n email,\n role: \"user\" as const,\n };\n })\n .build();\n },\n updateUserRoleWithSession: function (\n this: AuthServiceContext,\n sessionId: string,\n userId: string,\n role: \"user\" | \"admin\",\n ) {\n const now = new Date();\n return this.serviceTx(authSchema)\n .retrieve((uow) =>\n uow.findFirst(\"session\", (b) =>\n b\n .whereIndex(\"primary\", (eb) => eb(\"id\", \"=\", sessionId))\n .join((j) => j.sessionOwner((b) => b.select([\"id\", \"email\", \"role\"]))),\n ),\n )\n .mutate(({ uow, retrieveResult: [session] }) => {\n if (!session || !session.sessionOwner) {\n return { ok: false as const, code: \"session_invalid\" as const };\n }\n\n if (session.expiresAt < now) {\n uow.delete(\"session\", session.id, (b) => b.check());\n return { ok: false as const, code: \"session_invalid\" as const };\n }\n\n if (session.sessionOwner.role !== \"admin\") {\n return { ok: false as const, code: \"permission_denied\" as const };\n }\n\n uow.update(\"user\", userId, (b) => b.set({ role }));\n return { ok: true as const };\n })\n .build();\n },\n changePasswordWithSession: function (\n this: AuthServiceContext,\n sessionId: string,\n passwordHash: string,\n ) {\n const now = new Date();\n return this.serviceTx(authSchema)\n .retrieve((uow) =>\n uow.findFirst(\"session\", (b) =>\n b\n .whereIndex(\"primary\", (eb) => eb(\"id\", \"=\", sessionId))\n .join((j) => j.sessionOwner((b) => b.select([\"id\", \"email\", \"role\"]))),\n ),\n )\n .mutate(({ uow, retrieveResult: [session] }) => {\n if (!session || !session.sessionOwner) {\n return { ok: false as const, code: \"session_invalid\" as const };\n }\n\n if (session.expiresAt < now) {\n uow.delete(\"session\", session.id, (b) => b.check());\n return { ok: false as const, code: \"session_invalid\" as const };\n }\n\n uow.update(\"user\", session.sessionOwner.id, (b) => b.set({ passwordHash }).check());\n return { ok: true as const };\n })\n .build();\n },\n };\n}\n\nexport const userActionsRoutesFactory = defineRoutes<typeof authFragmentDefinition>().create(\n ({ services, config }) => {\n return [\n defineRoute({\n method: \"PATCH\",\n path: \"/users/:userId/role\",\n inputSchema: z.object({\n role: z.enum([\"user\", \"admin\"]),\n }),\n outputSchema: z.object({\n success: z.boolean(),\n }),\n errorCodes: [\"invalid_input\", \"session_invalid\", \"permission_denied\"],\n handler: async function ({ input, pathParams, headers, query }, { json, error }) {\n const { role } = await input.valid();\n const { userId } = pathParams;\n\n const sessionId = extractSessionId(headers, query.get(\"sessionId\"));\n\n if (!sessionId) {\n return error({ message: \"Session ID required\", code: \"session_invalid\" }, 400);\n }\n\n const [result] = await this.handlerTx()\n .withServiceCalls(() => [services.updateUserRoleWithSession(sessionId, userId, role)])\n .execute();\n\n if (!result.ok) {\n if (result.code === \"permission_denied\") {\n return error({ message: \"Unauthorized\", code: \"permission_denied\" }, 401);\n }\n\n return error({ message: \"Invalid session\", code: \"session_invalid\" }, 401);\n }\n\n return json({ success: true });\n },\n }),\n\n defineRoute({\n method: \"POST\",\n path: \"/sign-up\",\n inputSchema: z.object({\n email: z.email(),\n password: z.string().min(8).max(100),\n }),\n outputSchema: z.object({\n sessionId: z.string(),\n userId: z.string(),\n email: z.string(),\n role: z.enum([\"user\", \"admin\"]),\n }),\n errorCodes: [\"email_already_exists\", \"invalid_input\"],\n handler: async function ({ input }, { json, error }) {\n const { email, password } = await input.valid();\n\n const passwordHash = await hashPassword(password);\n\n const [result] = await this.handlerTx()\n .withServiceCalls(() => [services.signUpWithSession(email, passwordHash)])\n .execute();\n\n if (!result.ok) {\n return error({ message: \"Email already exists\", code: \"email_already_exists\" }, 400);\n }\n\n // Build response with Set-Cookie header\n const setCookieHeader = buildSetCookieHeader(result.sessionId, config.cookieOptions);\n\n return json(\n {\n sessionId: result.sessionId,\n userId: result.userId,\n email: result.email,\n role: result.role,\n },\n {\n headers: {\n \"Set-Cookie\": setCookieHeader,\n },\n },\n );\n },\n }),\n\n defineRoute({\n method: \"POST\",\n path: \"/sign-in\",\n inputSchema: z.object({\n email: z.email(),\n password: z.string().min(8).max(100),\n }),\n outputSchema: z.object({\n sessionId: z.string(),\n userId: z.string(),\n email: z.string(),\n role: z.enum([\"user\", \"admin\"]),\n }),\n errorCodes: [\"invalid_credentials\"],\n handler: async function ({ input }, { json, error }) {\n const { email, password } = await input.valid();\n\n // Get user by email\n const [user] = await this.handlerTx()\n .withServiceCalls(() => [services.getUserByEmail(email)])\n .execute();\n if (!user) {\n return error({ message: \"Invalid credentials\", code: \"invalid_credentials\" }, 401);\n }\n\n // Verify password\n const isValid = await verifyPassword(password, user.passwordHash);\n if (!isValid) {\n return error({ message: \"Invalid credentials\", code: \"invalid_credentials\" }, 401);\n }\n\n // Create session\n const [session] = await this.handlerTx()\n .withServiceCalls(() => [services.createSession(user.id)])\n .execute();\n\n // Build response with Set-Cookie header\n const setCookieHeader = buildSetCookieHeader(session.id, config.cookieOptions);\n\n return json(\n {\n sessionId: session.id,\n userId: user.id,\n email: user.email,\n role: user.role,\n },\n {\n headers: {\n \"Set-Cookie\": setCookieHeader,\n },\n },\n );\n },\n }),\n\n defineRoute({\n method: \"POST\",\n path: \"/change-password\",\n inputSchema: z.object({\n newPassword: z.string().min(8).max(100),\n }),\n outputSchema: z.object({\n success: z.boolean(),\n }),\n errorCodes: [\"session_invalid\"],\n handler: async function ({ input, headers, query }, { json, error }) {\n const { newPassword } = await input.valid();\n\n const sessionId = extractSessionId(headers, query.get(\"sessionId\"));\n\n if (!sessionId) {\n return error({ message: \"Session ID required\", code: \"session_invalid\" }, 400);\n }\n\n const passwordHash = await hashPassword(newPassword);\n\n const [result] = await this.handlerTx()\n .withServiceCalls(() => [services.changePasswordWithSession(sessionId, passwordHash)])\n .execute();\n\n if (!result.ok) {\n return error({ message: \"Invalid session\", code: \"session_invalid\" }, 401);\n }\n\n return json({ success: true });\n },\n }),\n ];\n },\n);\n","import { defineRoute, defineRoutes } from \"@fragno-dev/core\";\nimport type { DatabaseServiceContext } from \"@fragno-dev/db\";\nimport { authSchema } from \"../schema\";\nimport { z } from \"zod\";\nimport {\n buildClearCookieHeader,\n buildSetCookieHeader,\n extractSessionId,\n type CookieOptions,\n} from \"../utils/cookie\";\nimport type { Role, authFragmentDefinition } from \"..\";\n\ntype AuthServiceContext = DatabaseServiceContext<{}>;\n\nexport function createSessionServices(cookieOptions?: CookieOptions) {\n const services = {\n buildSessionCookie: function (sessionId: string): string {\n return buildSetCookieHeader(sessionId, cookieOptions);\n },\n createSession: function (this: AuthServiceContext, userId: string) {\n const expiresAt = new Date();\n expiresAt.setDate(expiresAt.getDate() + 30); // 30 days from now\n\n return this.serviceTx(authSchema)\n .mutate(({ uow }) => {\n const id = uow.create(\"session\", {\n userId,\n expiresAt,\n });\n\n return {\n id: id.valueOf(),\n userId,\n expiresAt,\n };\n })\n .build();\n },\n validateSession: function (this: AuthServiceContext, sessionId: string) {\n const now = new Date();\n return this.serviceTx(authSchema)\n .retrieve((uow) =>\n uow.findFirst(\"session\", (b) =>\n b\n .whereIndex(\"primary\", (eb) => eb(\"id\", \"=\", sessionId))\n .join((j) => j.sessionOwner((b) => b.select([\"id\", \"email\", \"role\"]))),\n ),\n )\n .mutate(({ uow, retrieveResult: [session] }) => {\n if (!session) {\n return null;\n }\n\n // Check if session has expired\n if (session.expiresAt < now) {\n uow.delete(\"session\", session.id, (b) => b.check());\n return null;\n }\n\n if (!session.sessionOwner) {\n return null;\n }\n\n return {\n id: session.id.valueOf(),\n userId: session.userId as unknown as string,\n user: {\n id: session.sessionOwner.id.valueOf(),\n email: session.sessionOwner.email,\n role: session.sessionOwner.role,\n },\n };\n })\n .build();\n },\n invalidateSession: function (this: AuthServiceContext, sessionId: string) {\n return this.serviceTx(authSchema)\n .retrieve((uow) =>\n uow.findFirst(\"session\", (b) =>\n b.whereIndex(\"primary\", (eb) => eb(\"id\", \"=\", sessionId)),\n ),\n )\n .mutate(({ uow, retrieveResult: [session] }) => {\n if (!session) {\n return false;\n }\n\n uow.delete(\"session\", session.id, (b) => b.check());\n return true;\n })\n .build();\n },\n getSession: function (this: AuthServiceContext, headers: Headers) {\n const sessionId = extractSessionId(headers);\n const now = new Date();\n\n return this.serviceTx(authSchema)\n .retrieve((uow) =>\n uow.findFirst(\"session\", (b) =>\n b\n .whereIndex(\"primary\", (eb) => eb(\"id\", \"=\", sessionId ?? \"\"))\n .join((j) => j.sessionOwner((b) => b.select([\"id\", \"email\", \"role\"]))),\n ),\n )\n .mutate(({ uow, retrieveResult: [session] }) => {\n if (!session || !sessionId) {\n return undefined;\n }\n\n if (session.expiresAt < now) {\n uow.delete(\"session\", session.id, (b) => b.check());\n return undefined;\n }\n\n if (!session.sessionOwner) {\n return undefined;\n }\n\n return {\n userId: session.sessionOwner.id.valueOf(),\n email: session.sessionOwner.email,\n };\n })\n .build();\n },\n };\n return services;\n}\n\nexport const sessionRoutesFactory = defineRoutes<typeof authFragmentDefinition>().create(\n ({ services, config }) => {\n return [\n defineRoute({\n method: \"POST\",\n path: \"/sign-out\",\n inputSchema: z\n .object({\n sessionId: z.string().optional(),\n })\n .optional(),\n outputSchema: z.object({\n success: z.boolean(),\n }),\n errorCodes: [\"session_not_found\"],\n handler: async function ({ input, headers }, { json, error }) {\n const body = await input.valid();\n\n // Extract session ID from cookies first, then body\n const sessionId = extractSessionId(headers, null, body?.sessionId);\n\n if (!sessionId) {\n return error({ message: \"Session ID required\", code: \"session_not_found\" }, 400);\n }\n\n const [success] = await this.handlerTx()\n .withServiceCalls(() => [services.invalidateSession(sessionId)])\n .execute();\n\n // Build response with clear cookie header\n const clearCookieHeader = buildClearCookieHeader(config.cookieOptions ?? {});\n\n if (!success) {\n // Still clear the cookie even if session not found in DB\n return json(\n { success: false },\n {\n headers: {\n \"Set-Cookie\": clearCookieHeader,\n },\n },\n );\n }\n\n return json(\n { success: true },\n {\n headers: {\n \"Set-Cookie\": clearCookieHeader,\n },\n },\n );\n },\n }),\n\n defineRoute({\n method: \"GET\",\n path: \"/me\",\n queryParameters: [\"sessionId\"],\n outputSchema: z.object({\n userId: z.string(),\n email: z.string(),\n role: z.enum([\"user\", \"admin\"]),\n }),\n errorCodes: [\"session_invalid\"],\n handler: async function ({ query, headers }, { json, error }) {\n // Extract session ID from cookies first, then query params\n const sessionId = extractSessionId(headers, query.get(\"sessionId\"));\n\n if (!sessionId) {\n return error({ message: \"Session ID required\", code: \"session_invalid\" }, 400);\n }\n\n const [session] = await this.handlerTx()\n .withServiceCalls(() => [services.validateSession(sessionId)])\n .execute();\n\n if (!session) {\n return error({ message: \"Invalid session\", code: \"session_invalid\" }, 401);\n }\n\n return json({\n userId: session.user.id,\n email: session.user.email,\n role: session.user.role as Role,\n });\n },\n }),\n ];\n },\n);\n","import { defineRoute, defineRoutes } from \"@fragno-dev/core\";\nimport type { DatabaseServiceContext } from \"@fragno-dev/db\";\nimport { type Cursor, decodeCursor } from \"@fragno-dev/db/cursor\";\nimport { authSchema } from \"../schema\";\nimport { z } from \"zod\";\nimport type { authFragmentDefinition } from \"..\";\n\nexport type SortField = \"email\" | \"createdAt\";\nexport type SortOrder = \"asc\" | \"desc\";\n\nexport interface GetUsersParams {\n search?: string;\n sortBy: SortField;\n sortOrder: SortOrder;\n pageSize: number;\n cursor?: Cursor;\n}\n\nexport interface UserResult {\n id: string;\n email: string;\n role: \"user\" | \"admin\";\n createdAt: Date;\n}\n\ntype AuthServiceContext = DatabaseServiceContext<{}>;\n\nexport function createUserOverviewServices() {\n const mapUser = (user: {\n id: unknown;\n email: string;\n role: string;\n createdAt: Date;\n }): UserResult => ({\n id: String(user.id),\n email: user.email,\n role: user.role as \"user\" | \"admin\",\n createdAt: user.createdAt,\n });\n\n return {\n getUsersWithCursor: function (this: AuthServiceContext, params: GetUsersParams) {\n const { search, sortBy, sortOrder, pageSize, cursor } = params;\n\n // Determine which index to use based on search and sortBy\n // When searching, only email sorting is allowed (search uses email index)\n const effectiveSortBy: SortField = search ? \"email\" : sortBy;\n const indexName = effectiveSortBy === \"email\" ? \"idx_user_email\" : \"idx_user_createdAt\";\n\n // If cursor is provided, extract its metadata to ensure consistency\n const effectiveSortOrder = cursor ? cursor.orderDirection : sortOrder;\n const effectivePageSize = cursor ? cursor.pageSize : pageSize;\n\n return this.serviceTx(authSchema)\n .retrieve((uow) =>\n uow.findWithCursor(\"user\", (b) => {\n // When searching, we must filter by email and can only use the email index\n if (search) {\n const query = b\n .whereIndex(\"idx_user_email\", (eb) => eb(\"email\", \"contains\", search))\n .orderByIndex(\"idx_user_email\", effectiveSortOrder)\n .pageSize(effectivePageSize);\n\n // Add cursor for pagination continuation\n return cursor ? query.after(cursor) : query;\n }\n\n // When not searching, use the appropriate index for sorting\n const query = b\n .whereIndex(indexName)\n .orderByIndex(indexName, effectiveSortOrder)\n .pageSize(effectivePageSize);\n\n // Add cursor for pagination continuation\n return cursor ? query.after(cursor) : query;\n }),\n )\n .transformRetrieve(([result]) => ({\n users: result.items.map(mapUser),\n cursor: result.cursor,\n hasNextPage: result.hasNextPage,\n }))\n .build();\n },\n };\n}\n\nconst sortBySchema = z.enum([\"email\", \"createdAt\"]);\nconst sortOrderSchema = z.enum([\"asc\", \"desc\"]);\n\nexport const userOverviewRoutesFactory = defineRoutes<typeof authFragmentDefinition>().create(\n ({ services }) => {\n const querySchema = z.object({\n search: z.string().optional(),\n sortBy: sortBySchema.default(\"createdAt\"),\n sortOrder: sortOrderSchema.default(\"desc\"),\n pageSize: z.coerce.number().int().min(1).max(100).default(20),\n });\n\n const parseCursor = (cursorParam: string | null): Cursor | undefined => {\n if (!cursorParam) {\n return undefined;\n }\n try {\n return decodeCursor(cursorParam);\n } catch {\n return undefined;\n }\n };\n\n return [\n defineRoute({\n method: \"GET\",\n path: \"/users\",\n queryParameters: [\"search\", \"sortBy\", \"sortOrder\", \"pageSize\", \"cursor\"],\n outputSchema: z.object({\n users: z.array(\n z.object({\n id: z.string(),\n email: z.string(),\n role: z.enum([\"user\", \"admin\"]),\n createdAt: z.string(),\n }),\n ),\n cursor: z.string().optional(),\n hasNextPage: z.boolean(),\n sortBy: sortBySchema,\n }),\n errorCodes: [\"invalid_input\"],\n handler: async function ({ query }, { json, error }) {\n const parsed = querySchema.safeParse(Object.fromEntries(query.entries()));\n if (!parsed.success) {\n return error({ message: \"Invalid query parameters\", code: \"invalid_input\" }, 400);\n }\n\n const rawSearch = parsed.data.search?.trim();\n const search = rawSearch ? rawSearch : undefined;\n const sortBy: SortField = search ? \"email\" : parsed.data.sortBy;\n const params = {\n search,\n sortBy,\n sortOrder: parsed.data.sortOrder as SortOrder,\n pageSize: parsed.data.pageSize,\n };\n const cursor = parseCursor(query.get(\"cursor\"));\n\n const [result] = await this.handlerTx()\n .withServiceCalls(() => [services.getUsersWithCursor({ ...params, cursor })])\n .execute();\n\n return json({\n users: result.users.map((user) => ({\n id: user.id,\n email: user.email,\n role: user.role,\n createdAt: user.createdAt.toISOString(),\n })),\n cursor: result.cursor?.encode(),\n hasNextPage: result.hasNextPage,\n sortBy: params.sortBy, // Return the actual sortBy used (may differ from requested)\n });\n },\n }),\n ];\n },\n);\n","import { defineFragment, instantiate } from \"@fragno-dev/core\";\nimport { createClientBuilder, type FragnoPublicClientConfig } from \"@fragno-dev/core/client\";\nimport { withDatabase, type FragnoPublicConfigWithDatabase } from \"@fragno-dev/db\";\nimport { authSchema } from \"./schema\";\nimport { createUserServices, userActionsRoutesFactory } from \"./user/user-actions\";\nimport { createSessionServices, sessionRoutesFactory } from \"./session/session\";\nimport {\n createUserOverviewServices,\n userOverviewRoutesFactory,\n type GetUsersParams,\n type UserResult,\n type SortField,\n type SortOrder,\n} from \"./user/user-overview\";\nimport type { CookieOptions } from \"./utils/cookie\";\n\nexport interface AuthConfig {\n sendEmail?: (params: { to: string; subject: string; body: string }) => Promise<void>;\n cookieOptions?: CookieOptions;\n}\n\nexport const authFragmentDefinition = defineFragment<AuthConfig>(\"auth\")\n .extend(withDatabase(authSchema))\n .providesBaseService(({ defineService, config }) => {\n return defineService({\n ...createUserServices(),\n ...createSessionServices(config.cookieOptions),\n ...createUserOverviewServices(),\n });\n })\n .build();\n\nexport type AuthFragment = typeof authFragmentDefinition;\n\nexport function createAuthFragment(\n config: AuthConfig = {},\n fragnoConfig: FragnoPublicConfigWithDatabase,\n) {\n const options = {\n ...fragnoConfig,\n // Preserve legacy namespace to avoid changing physical table names.\n databaseNamespace:\n fragnoConfig.databaseNamespace !== undefined\n ? fragnoConfig.databaseNamespace\n : \"simple-auth-db\",\n };\n\n return instantiate(authFragmentDefinition)\n .withConfig(config)\n .withOptions(options)\n .withRoutes([userActionsRoutesFactory, sessionRoutesFactory, userOverviewRoutesFactory])\n .build();\n}\n\nexport function createAuthFragmentClients(fragnoConfig?: FragnoPublicClientConfig) {\n // Note: Cookies are automatically sent for same-origin requests by the browser.\n // For cross-origin requests, you may need to configure CORS headers on the server.\n const config = { ...fragnoConfig };\n\n const b = createClientBuilder(\n authFragmentDefinition,\n config,\n [userActionsRoutesFactory, sessionRoutesFactory, userOverviewRoutesFactory],\n {\n type: \"options\",\n options: {\n credentials: \"include\",\n },\n },\n );\n\n const useMe = b.createHook(\"/me\");\n const useSignUp = b.createMutator(\"POST\", \"/sign-up\");\n const useSignIn = b.createMutator(\"POST\", \"/sign-in\");\n const useSignOut = b.createMutator(\"POST\", \"/sign-out\", (invalidate) => {\n invalidate(\"GET\", \"/me\", {});\n invalidate(\"GET\", \"/users\", {});\n });\n const useUsers = b.createHook(\"/users\");\n const useUpdateUserRole = b.createMutator(\"PATCH\", \"/users/:userId/role\", (invalidate) => {\n invalidate(\"GET\", \"/users\", {});\n invalidate(\"GET\", \"/me\", {});\n });\n const useChangePassword = b.createMutator(\"POST\", \"/change-password\");\n\n return {\n // Reactive hooks - Auth\n useSignUp,\n useSignIn,\n useSignOut,\n useMe,\n useUsers,\n useUpdateUserRole,\n useChangePassword,\n\n // Non-reactive methods\n signIn: {\n email: async ({\n email,\n password,\n rememberMe: _rememberMe,\n }: {\n email: string;\n password: string;\n rememberMe?: boolean;\n }) => {\n // Note: rememberMe is accepted but not yet implemented on the backend\n return useSignIn.mutateQuery({\n body: {\n email,\n password,\n },\n });\n },\n },\n\n signUp: {\n email: async ({ email, password }: { email: string; password: string }) => {\n return useSignUp.mutateQuery({\n body: {\n email,\n password,\n },\n });\n },\n },\n\n signOut: (params?: { sessionId?: string }) => {\n return useSignOut.mutateQuery({\n body: params?.sessionId ? { sessionId: params.sessionId } : {},\n });\n },\n\n me: async (params?: { sessionId?: string }) => {\n if (params?.sessionId) {\n return useMe.query({ query: { sessionId: params.sessionId } });\n }\n\n return useMe.query();\n },\n };\n}\n\nexport type { FragnoRouteConfig } from \"@fragno-dev/core/api\";\nexport type { GetUsersParams, UserResult, SortField, SortOrder };\n\nexport type Role = \"user\" | \"admin\";\n"],"mappings":"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"}
|