@datrix/api 0.1.0 → 0.1.2

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/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/api.ts","../src/auth/password.ts","../src/auth/error-helper.ts","../src/auth/jwt.ts","../src/auth/types.ts","../src/auth/session.ts","../src/auth/manager.ts","../src/handler/auth-handler.ts","../src/handler/utils.ts","../src/errors/api-error.ts","../src/errors/auth-error.ts","../src/middleware/permission.ts","../src/parser/query-parser.ts","../src/parser/fields-parser.ts","../src/parser/errors.ts","../src/parser/where-parser.ts","../src/parser/populate-parser.ts","../src/middleware/context.ts","../src/handler/unified.ts","../src/helper/index.ts","../src/middleware/auth.ts","../src/serializer/query.ts"],"sourcesContent":["/**\n * Datrix API Module - Plugin Edition\n *\n * Provides REST API functionality as a Datrix plugin with integrated authentication system.\n *\n * @example\n * ```ts\n * import { createApiPlugin } from '@datrix/api';\n *\n * const apiPlugin = createApiPlugin({\n * enabled: true,\n * prefix: '/api',\n * auth: {\n * enabled: true,\n * jwt: { secret: process.env.JWT_SECRET }\n * }\n * });\n *\n * export default defineConfig(() => ({\n * adapter: new PostgresAdapter({ ... }),\n * schemas: [userSchema],\n * plugins: [apiPlugin]\n * }));\n * ```\n */\n\n// Main exports - Plugin API\nexport { ApiPlugin } from \"./api\";\n\n// Auth exports\nexport { MemorySessionStore } from \"./auth/session\";\n\nexport { handleRequest } from \"./helper\";\n\n// Middleware module (auth, context, permission)\nexport * from \"./middleware\";\n\n// Parser module (query string parsing)\nexport * from \"./parser\";\n\n// Serializer module (response serialization)\nexport * from \"./serializer\";\n\n// Handler module (CRUD and auth handlers)\nexport * from \"./handler\";\n","/**\n * API Plugin\n *\n * Transforms the API package into a Datrix plugin.\n * Manages authentication schema, user sync, and auth routes.\n */\n\nimport { BasePlugin } from \"@datrix/core\";\nimport type {\n\tPluginContext,\n\tQueryContext,\n\tSchemaDefinition,\n} from \"@datrix/core\";\nimport { DefaultPermission, defineSchema } from \"@datrix/core\";\nimport { DEFAULT_API_AUTH_CONFIG } from \"@datrix/core\";\nimport { AuthManager } from \"./auth/manager\";\nimport { createUnifiedAuthHandler } from \"./handler/auth-handler\";\nimport { handleCrudRequest } from \"./handler/unified\";\nimport { handlerError } from \"./errors/api-error\";\nimport { ApiConfig } from \"./types\";\nimport { Datrix } from \"@datrix/core\";\nimport type { IApiPlugin } from \"@datrix/core\";\nimport type { DatrixEntry, DatrixRecord } from \"@datrix/core\";\nimport { datrixErrorResponse } from \"./handler/utils\";\nimport type { AuthUser, IUpload } from \"@datrix/core\";\nimport { QueryObject } from \"@datrix/core\";\nimport { FallbackInput } from \"@datrix/core\";\n\nexport class ApiPlugin<TRole extends string = string>\n\textends BasePlugin<ApiConfig<TRole>>\n\timplements IApiPlugin<TRole> {\n\treadonly name = \"api\";\n\treadonly version = \"1.0.0\";\n\n\tpublic authManager?: AuthManager;\n\tpublic user: AuthUser | null = null;\n\tprivate datrixInstance?: Datrix;\n\n\tpublic get datrix(): Datrix {\n\t\treturn this.datrixInstance as Datrix;\n\t}\n\n\tpublic get upload(): IUpload | undefined {\n\t\treturn this.options.upload;\n\t}\n\n\tpublic setUser(user: AuthUser | null) {\n\t\tthis.user = user;\n\t}\n\n\tprivate get authConfig(): ApiConfig<TRole>[\"auth\"] | undefined {\n\t\treturn this.options.auth;\n\t}\n\n\tprivate get apiConfig(): ApiConfig<TRole> {\n\t\treturn this.options;\n\t}\n\n\tprivate get authSchemaName(): string {\n\t\treturn this.authConfig?.authSchemaName ?? \"authentication\";\n\t}\n\n\tprivate get userSchemaName(): string {\n\t\treturn this.authConfig?.userSchema?.name ?? \"user\";\n\t}\n\n\tprivate get userSchemaEmailField(): string {\n\t\treturn this.authConfig?.userSchema?.email ?? \"email\";\n\t}\n\n\tpublic get authDefaultPermission(): DefaultPermission<TRole> | undefined {\n\t\treturn this.authConfig?.defaultPermission;\n\t}\n\n\tpublic get authDefaultRole(): TRole | undefined {\n\t\treturn this.authConfig?.defaultRole;\n\t}\n\n\tpublic get excludeSchemas(): readonly string[] {\n\t\treturn [\n\t\t\t...(this.apiConfig.excludeSchemas ?? []),\n\t\t\t\"_datrix\",\n\t\t\t\"_datrix_migrations\",\n\t\t];\n\t}\n\n\tprivate getTableName(schemaName: string): string {\n\t\tconst schema = this.datrix.getSchema(schemaName);\n\t\treturn schema?.tableName || `${schemaName.toLowerCase()}s`;\n\t}\n\n\toverride async onCreateQueryContext(\n\t\tcontext: QueryContext,\n\t): Promise<QueryContext> {\n\t\t// Add authenticated user to context metadata\n\t\tif (this.user) {\n\t\t\tcontext.user = this.user;\n\t\t}\n\n\t\treturn context;\n\t}\n\n\tasync init(context: PluginContext): Promise<void> {\n\t\tthis.context = context;\n\n\t\t// Auth is disabled if authConfig is undefined\n\t\tif (!this.authConfig) {\n\t\t\treturn;\n\t\t}\n\n\t\tif (context.schemas.has(\"auth\")) {\n\t\t\tthrow this.createError(\n\t\t\t\t\"Schema name 'auth' is reserved for API authentication routes\",\n\t\t\t\t\"RESERVED_SCHEMA_NAME\",\n\t\t\t);\n\t\t}\n\n\t\tif (!context.schemas.has(this.userSchemaName)) {\n\t\t\tthrow this.createError(\n\t\t\t\t`User schema '${this.userSchemaName}' not found. Create it before enabling auth.`,\n\t\t\t\t\"USER_SCHEMA_NOT_FOUND\",\n\t\t\t);\n\t\t}\n\n\t\tconst userSchema = context.schemas.get(this.userSchemaName);\n\t\tconst emailField = this.userSchemaEmailField;\n\t\tif (!userSchema?.fields[emailField]) {\n\t\t\tthrow this.createError(\n\t\t\t\t`User schema must have an '${emailField}' field`,\n\t\t\t\t\"MISSING_EMAIL_FIELD\",\n\t\t\t);\n\t\t}\n\n\t\tif (this.authConfig.jwt) {\n\t\t\tif (this.authConfig.jwt.secret.length < 32) {\n\t\t\t\tthrow this.createError(\n\t\t\t\t\t\"JWT secret must be at least 32 characters long for security\",\n\t\t\t\t\t\"WEAK_JWT_SECRET\",\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\tthis.authManager = new AuthManager(this.authConfig);\n\t}\n\n\tasync destroy(): Promise<void> { }\n\n\toverride async getSchemas(): Promise<SchemaDefinition[]> {\n\t\tconst schemas: SchemaDefinition[] = [];\n\n\t\tif (this.options.upload) {\n\t\t\tconst uploadSchemas = await this.options.upload.getSchemas();\n\t\t\tschemas.push(...uploadSchemas);\n\t\t}\n\n\t\tif (!this.authConfig) {\n\t\t\treturn schemas;\n\t\t}\n\n\t\tconst authSchema = defineSchema({\n\t\t\tname: this.authSchemaName,\n\t\t\tfields: {\n\t\t\t\tuser: {\n\t\t\t\t\ttype: \"relation\",\n\t\t\t\t\trequired: true,\n\t\t\t\t\tkind: \"belongsTo\",\n\t\t\t\t\tmodel: this.userSchemaName,\n\t\t\t\t},\n\t\t\t\temail: {\n\t\t\t\t\ttype: \"string\",\n\t\t\t\t\trequired: true,\n\t\t\t\t},\n\t\t\t\tpassword: {\n\t\t\t\t\ttype: \"string\",\n\t\t\t\t\trequired: true,\n\t\t\t\t},\n\t\t\t\tpasswordSalt: {\n\t\t\t\t\ttype: \"string\",\n\t\t\t\t\trequired: true,\n\t\t\t\t},\n\t\t\t\trole: {\n\t\t\t\t\ttype: \"string\",\n\t\t\t\t\trequired: true,\n\t\t\t\t\tdefault: this.authDefaultRole ?? \"user\",\n\t\t\t\t},\n\t\t\t},\n\t\t\tindexes: [\n\t\t\t\t{\n\t\t\t\t\tname: `${this.authSchemaName}_email_idx`,\n\t\t\t\t\tfields: [\"email\"],\n\t\t\t\t\tunique: true,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tname: `${this.authSchemaName}_userId_idx`,\n\t\t\t\t\tfields: [\"user\"],\n\t\t\t\t\tunique: true,\n\t\t\t\t},\n\t\t\t],\n\t\t});\n\n\t\tschemas.push(authSchema);\n\t\treturn schemas;\n\t}\n\n\toverride async onBeforeQuery<T extends DatrixEntry>(\n\t\tquery: QueryObject<T>,\n\t\tcontext: QueryContext,\n\t): Promise<QueryObject<T>> {\n\t\tif (!this.authConfig) {\n\t\t\treturn query;\n\t\t}\n\n\t\tconst userTable = this.getTableName(this.userSchemaName);\n\n\t\t// User insert → store flag in metadata\n\t\tif (query.type === \"insert\" && query.table === userTable) {\n\t\t\tcontext.metadata[\"api:createAuth\"] = true;\n\t\t\tcontext.metadata[\"api:userData\"] = query.data[0];\n\t\t}\n\n\t\t// User email update → store flag in metadata\n\t\tif (query.type === \"update\" && query.table === userTable) {\n\t\t\tconst data = query.data;\n\t\t\tconst emailField = this.userSchemaEmailField;\n\t\t\tif (data && emailField in data) {\n\t\t\t\tcontext.metadata[\"api:syncEmail\"] = (\n\t\t\t\t\tquery.data as Record<string, unknown>\n\t\t\t\t)[emailField];\n\t\t\t\tcontext.metadata[\"api:userId\"] = query.where?.[\"id\"];\n\t\t\t}\n\t\t}\n\n\t\treturn query;\n\t}\n\n\toverride async onAfterQuery<TResult>(\n\t\tresult: TResult,\n\t\tcontext: QueryContext,\n\t): Promise<TResult> {\n\t\tif (!this.authConfig) {\n\t\t\treturn result;\n\t\t}\n\n\t\tconst pluginContext = this.getContext();\n\n\t\t// User created → create authentication record\n\t\tif (context.metadata[\"api:createAuth\"]) {\n\t\t\tconst { id: userId } = Array.isArray(result) ? result[0] : result;\n\t\t\tif (typeof userId === \"number\") {\n\t\t\t\tconst user: Partial<DatrixRecord> = {\n\t\t\t\t\t...(context.metadata[\"api:userData\"] as Record<string, unknown>),\n\t\t\t\t\tuserId,\n\t\t\t\t};\n\t\t\t\tawait this.createAuthenticationRecord(user, pluginContext);\n\t\t\t}\n\t\t}\n\n\t\t// User email updated → sync authentication email\n\t\tif (context.metadata[\"api:syncEmail\"] && context.metadata[\"api:userId\"]) {\n\t\t\tconst newEmail = context.metadata[\"api:syncEmail\"] as string;\n\t\t\tconst userId = context.metadata[\"api:userId\"] as string;\n\t\t\tawait this.syncAuthenticationEmail(userId, newEmail, pluginContext);\n\t\t}\n\n\t\treturn result;\n\t}\n\n\tprivate async createAuthenticationRecord(\n\t\t_user: Partial<DatrixRecord>,\n\t\t_context: PluginContext,\n\t): Promise<void> {\n\t\tconst emailField = this.userSchemaEmailField;\n\t\tconst user = _user as FallbackInput;\n\t\tconst authData: FallbackInput = {\n\t\t\tuser: user[\"userId\"]!,\n\t\t\temail: user[emailField]!,\n\t\t\tpassword: user[\"password\"] || \"\",\n\t\t\tpasswordSalt: user[\"passwordSalt\"] || \"\",\n\t\t\trole: user[\"role\"] || this.authConfig?.defaultRole || \"user\",\n\t\t};\n\n\t\tawait this.datrixInstance!.raw.create(this.authSchemaName, authData);\n\t}\n\n\tprivate async syncAuthenticationEmail(\n\t\tuserId: string,\n\t\tnewEmail: string,\n\t\t_context: PluginContext,\n\t): Promise<void> {\n\t\tawait this.datrix.raw.updateMany(\n\t\t\tthis.authSchemaName,\n\t\t\t{ user: { id: { $eq: userId } } },\n\t\t\t{ email: newEmail },\n\t\t);\n\t}\n\n\t/**\n\t * Handle HTTP request\n\t *\n\t * Main entry point for all API requests.\n\t * Routes to auth handlers or CRUD handlers.\n\t */\n\tasync handleRequest(request: Request, datrix: Datrix): Promise<Response> {\n\t\tif (!this.isInitialized()) {\n\t\t\treturn datrixErrorResponse(\n\t\t\t\thandlerError.internalError(\"API plugin not initialized\"),\n\t\t\t);\n\t\t}\n\n\t\tthis.datrixInstance = datrix;\n\n\t\tconst url = new URL(request.url);\n\t\tconst prefix = this.apiConfig.prefix ?? \"/api\";\n\n\t\tif (!url.pathname.startsWith(prefix)) {\n\t\t\treturn datrixErrorResponse(\n\t\t\t\thandlerError.internalError(\"Invalid API prefix\"),\n\t\t\t);\n\t\t}\n\n\t\tconst pathAfterPrefix = url.pathname.slice(prefix.length);\n\t\tconst segments = pathAfterPrefix.split(\"/\").filter(Boolean);\n\t\tconst model = segments[0];\n\n\t\tif (this.authConfig && this.isAuthPath(pathAfterPrefix)) {\n\t\t\treturn this.handleAuthRequest(request, datrix);\n\t\t}\n\n\t\tif (\n\t\t\tmodel === \"upload\" &&\n\t\t\tthis.apiConfig.upload &&\n\t\t\trequest.method !== \"GET\"\n\t\t) {\n\t\t\treturn this.apiConfig.upload.handleRequest(request, datrix);\n\t\t}\n\n\t\treturn handleCrudRequest(request, datrix, this, {\n\t\t\tapiPrefix: prefix,\n\t\t});\n\t}\n\n\tprivate isAuthPath(pathname: string): boolean {\n\t\tconst e = this.authConfig?.endpoints;\n\t\tconst d = DEFAULT_API_AUTH_CONFIG.endpoints;\n\t\tconst login = e?.login ?? d.login;\n\t\tconst register = e?.register ?? d.register;\n\t\tconst logout = e?.logout ?? d.logout;\n\t\tconst me = e?.me ?? d.me;\n\n\t\tconst authPrefix =\n\t\t\t[login, register, logout, me].map((p) => p.split(\"/\")[1]).find(Boolean) ??\n\t\t\t\"auth\";\n\n\t\treturn (\n\t\t\tpathname.startsWith(`/${authPrefix}/`) || pathname === `/${authPrefix}`\n\t\t);\n\t}\n\n\t/**\n\t * Handle authentication requests\n\t */\n\tprivate async handleAuthRequest(\n\t\trequest: Request,\n\t\tdatrix: Datrix,\n\t): Promise<Response> {\n\t\tif (!this.authManager) {\n\t\t\treturn datrixErrorResponse(\n\t\t\t\thandlerError.internalError(\"Authentication not configured\"),\n\t\t\t);\n\t\t}\n\n\t\tconst handler = createUnifiedAuthHandler(\n\t\t\t{\n\t\t\t\tdatrix,\n\t\t\t\tauthManager: this.authManager,\n\t\t\t\tauthConfig: this.authConfig!,\n\t\t\t},\n\t\t\tthis.apiConfig.prefix ?? \"/api\",\n\t\t);\n\n\t\treturn handler(request);\n\t}\n\n\t/**\n\t * Check if API is enabled\n\t */\n\tisEnabled(): boolean {\n\t\treturn !(this.apiConfig.disabled ?? false);\n\t}\n\n\t/**\n\t * Check if authentication is enabled\n\t */\n\tisAuthEnabled(): boolean {\n\t\treturn this.authConfig !== undefined;\n\t}\n\n\t/**\n\t * Get auth manager (for external use)\n\t */\n\tgetAuthManager(): AuthManager | undefined {\n\t\treturn this.authManager;\n\t}\n}\n","/**\n * Password Utilities\n *\n * Handles password hashing and verification using PBKDF2\n */\n\nimport { pbkdf2Sync, randomBytes, timingSafeEqual } from \"node:crypto\";\nimport {\n\tthrowPasswordTooShort,\n\tthrowPasswordHashError,\n\tthrowPasswordVerifyError,\n} from \"./error-helper\";\n\n/**\n * Password hash result\n */\nexport interface PasswordHash {\n\treadonly hash: string;\n\treadonly salt: string;\n}\n\n/**\n * Password configuration\n */\nexport interface PasswordConfig {\n\treadonly iterations?: number; // PBKDF2 iterations (default: 100000)\n\treadonly keyLength?: number; // PBKDF2 key length (default: 64)\n\treadonly minLength?: number; // Minimum password length (default: 8)\n}\n\n/**\n * Default password configuration\n */\nconst DEFAULT_CONFIG: Required<PasswordConfig> = {\n\titerations: 100000,\n\tkeyLength: 64,\n\tminLength: 8,\n};\n\n/**\n * Password Manager\n *\n * Manages password hashing and verification\n */\nexport class PasswordManager {\n\tprivate readonly iterations: number;\n\tprivate readonly keyLength: number;\n\tprivate readonly minLength: number;\n\n\tconstructor(config: PasswordConfig = {}) {\n\t\tthis.iterations = config.iterations ?? DEFAULT_CONFIG.iterations;\n\t\tthis.keyLength = config.keyLength ?? DEFAULT_CONFIG.keyLength;\n\t\tthis.minLength = config.minLength ?? DEFAULT_CONFIG.minLength;\n\t}\n\n\t/**\n\t * Hash password using PBKDF2\n\t */\n\tasync hash(password: string): Promise<PasswordHash> {\n\t\t// Validate password strength\n\t\tif (!password || password.length < this.minLength) {\n\t\t\tthrowPasswordTooShort(this.minLength, password?.length ?? 0);\n\t\t}\n\n\t\ttry {\n\t\t\tconst salt = randomBytes(32).toString(\"hex\");\n\n\t\t\tconst hash = pbkdf2Sync(\n\t\t\t\tpassword,\n\t\t\t\tsalt,\n\t\t\t\tthis.iterations,\n\t\t\t\tthis.keyLength,\n\t\t\t\t\"sha512\",\n\t\t\t).toString(\"hex\");\n\n\t\t\treturn { hash, salt };\n\t\t} catch (error) {\n\t\t\tthrowPasswordHashError(error instanceof Error ? error : undefined);\n\t\t}\n\t}\n\n\t/**\n\t * Verify password against hash\n\t */\n\tasync verify(password: string, hash: string, salt: string): Promise<boolean> {\n\t\ttry {\n\t\t\tconst computedHash = pbkdf2Sync(\n\t\t\t\tpassword,\n\t\t\t\tsalt,\n\t\t\t\tthis.iterations,\n\t\t\t\tthis.keyLength,\n\t\t\t\t\"sha512\",\n\t\t\t).toString(\"hex\");\n\n\t\t\t// Constant-time comparison to prevent timing attacks\n\t\t\tconst isValid = this.constantTimeCompare(computedHash, hash);\n\n\t\t\treturn isValid;\n\t\t} catch (error) {\n\t\t\tthrowPasswordVerifyError(error instanceof Error ? error : undefined);\n\t\t}\n\t}\n\n\t/**\n\t * Constant-time string comparison (prevent timing attacks)\n\t */\n\tprivate constantTimeCompare(a: string, b: string): boolean {\n\t\tif (a.length !== b.length) {\n\t\t\treturn false;\n\t\t}\n\n\t\ttry {\n\t\t\tconst bufA = Buffer.from(a);\n\t\t\tconst bufB = Buffer.from(b);\n\t\t\treturn timingSafeEqual(bufA, bufB);\n\t\t} catch {\n\t\t\t// Fallback to manual constant-time comparison\n\t\t\tlet result = 0;\n\t\t\tfor (let i = 0; i < a.length; i++) {\n\t\t\t\tresult |= (a.charCodeAt(i) as number) ^ (b.charCodeAt(i) as number);\n\t\t\t}\n\t\t\treturn result === 0;\n\t\t}\n\t}\n}\n\n/**\n * Create password manager\n */\nexport function createPasswordManager(\n\tconfig?: PasswordConfig,\n): PasswordManager {\n\treturn new PasswordManager(config);\n}\n","/**\n * Auth Error Helper Functions\n *\n * Centralized error throwing functions for authentication module.\n * Provides consistent error messages and suggestions.\n */\n\nimport { DatrixAuthError } from \"@datrix/core\";\n\n// ============================================================================\n// JWT Errors\n// ============================================================================\n\n/**\n * Throw JWT sign error\n *\n * @param cause - Original error\n */\nexport function throwJwtSignError(cause?: Error): never {\n\tthrow new DatrixAuthError(\"Failed to sign JWT token\", {\n\t\tcode: \"JWT_SIGN_ERROR\",\n\t\tstrategy: \"jwt\",\n\t\tcause,\n\t\tsuggestion: \"Check your JWT configuration and secret key\",\n\t});\n}\n\n/**\n * Throw JWT verify error\n *\n * @param cause - Original error\n */\nexport function throwJwtVerifyError(cause?: Error): never {\n\tthrow new DatrixAuthError(\"Failed to verify JWT token\", {\n\t\tcode: \"JWT_VERIFY_ERROR\",\n\t\tstrategy: \"jwt\",\n\t\tcause,\n\t\tsuggestion: \"Ensure the token is valid and not tampered with\",\n\t});\n}\n\n/**\n * Throw JWT decode error\n *\n * @param cause - Original error\n */\nexport function throwJwtDecodeError(cause?: Error): never {\n\tthrow new DatrixAuthError(\"Failed to decode JWT token\", {\n\t\tcode: \"JWT_DECODE_ERROR\",\n\t\tstrategy: \"jwt\",\n\t\tcause,\n\t\tsuggestion: \"Ensure the token format is correct\",\n\t});\n}\n\n/**\n * Throw JWT invalid format error\n */\nexport function throwJwtInvalidFormat(): never {\n\tthrow new DatrixAuthError(\"Invalid JWT format\", {\n\t\tcode: \"JWT_INVALID_FORMAT\",\n\t\tstrategy: \"jwt\",\n\t\tsuggestion: \"JWT must be in format: header.payload.signature\",\n\t\texpected: \"three dot-separated base64url strings\",\n\t});\n}\n\n/**\n * Throw JWT invalid header error\n */\nexport function throwJwtInvalidHeader(): never {\n\tthrow new DatrixAuthError(\"Invalid JWT header\", {\n\t\tcode: \"JWT_INVALID_HEADER\",\n\t\tstrategy: \"jwt\",\n\t\tsuggestion: \"Ensure the JWT header has correct algorithm and type\",\n\t\texpected: 'header with typ: \"JWT\" and matching algorithm',\n\t});\n}\n\n/**\n * Throw JWT invalid payload error\n */\nexport function throwJwtInvalidPayload(): never {\n\tthrow new DatrixAuthError(\"Invalid JWT payload\", {\n\t\tcode: \"JWT_INVALID_PAYLOAD\",\n\t\tstrategy: \"jwt\",\n\t\tsuggestion: \"JWT payload must contain userId, role, iat, and exp fields\",\n\t\texpected: \"valid JWT payload with required fields\",\n\t});\n}\n\n/**\n * Throw JWT invalid signature error\n */\nexport function throwJwtInvalidSignature(): never {\n\tthrow new DatrixAuthError(\"Invalid JWT signature\", {\n\t\tcode: \"JWT_INVALID_SIGNATURE\",\n\t\tstrategy: \"jwt\",\n\t\tsuggestion: \"Token may have been tampered with or signed with wrong secret\",\n\t\texpected: \"valid HMAC signature\",\n\t});\n}\n\n/**\n * Throw JWT expired error\n *\n * @param exp - Token expiration timestamp\n * @param now - Current timestamp\n */\nexport function throwJwtExpired(exp: number, now: number): never {\n\tthrow new DatrixAuthError(\"JWT token expired\", {\n\t\tcode: \"JWT_EXPIRED\",\n\t\tstrategy: \"jwt\",\n\t\tcontext: { exp, now },\n\t\tsuggestion: \"Refresh your token or login again\",\n\t\texpected: \"token with exp > current time\",\n\t});\n}\n\n/**\n * Throw JWT invalid issued at time error\n */\nexport function throwJwtInvalidIat(): never {\n\tthrow new DatrixAuthError(\"JWT token issued in the future\", {\n\t\tcode: \"JWT_INVALID_IAT\",\n\t\tstrategy: \"jwt\",\n\t\tsuggestion: \"Check your server time synchronization\",\n\t\texpected: \"token with iat <= current time (allowing 60s skew)\",\n\t});\n}\n\n/**\n * Throw JWT invalid issuer error\n *\n * @param expected - Expected issuer\n * @param received - Received issuer\n */\nexport function throwJwtInvalidIssuer(\n\texpected: string,\n\treceived: string | undefined,\n): never {\n\tthrow new DatrixAuthError(\"JWT issuer mismatch\", {\n\t\tcode: \"JWT_INVALID_ISSUER\",\n\t\tstrategy: \"jwt\",\n\t\texpected,\n\t\treceived,\n\t\tsuggestion: `Token must be issued by: ${expected}`,\n\t});\n}\n\n/**\n * Throw JWT invalid audience error\n *\n * @param expected - Expected audience\n * @param received - Received audience\n */\nexport function throwJwtInvalidAudience(\n\texpected: string,\n\treceived: string | undefined,\n): never {\n\tthrow new DatrixAuthError(\"JWT audience mismatch\", {\n\t\tcode: \"JWT_INVALID_AUDIENCE\",\n\t\tstrategy: \"jwt\",\n\t\texpected,\n\t\treceived,\n\t\tsuggestion: `Token must be for audience: ${expected}`,\n\t});\n}\n\n// ============================================================================\n// Session Errors\n// ============================================================================\n\n/**\n * Throw session create error\n *\n * @param cause - Original error\n */\nexport function throwSessionCreateError(cause?: Error): never {\n\tthrow new DatrixAuthError(\"Failed to create session\", {\n\t\tcode: \"SESSION_CREATE_ERROR\",\n\t\tstrategy: \"session\",\n\t\tcause,\n\t\tsuggestion: \"Check your session store configuration\",\n\t});\n}\n\n/**\n * Throw session not found error\n *\n * @param sessionId - Session ID\n */\nexport function throwSessionNotFound(sessionId?: string): never {\n\tthrow new DatrixAuthError(\"Session not found\", {\n\t\tcode: \"AUTH_SESSION_NOT_FOUND\",\n\t\tstrategy: \"session\",\n\t\tcontext: { sessionId },\n\t\tsuggestion: \"Login again to create a new session\",\n\t});\n}\n\n/**\n * Throw session expired error\n *\n * @param sessionId - Session ID\n */\nexport function throwSessionExpired(sessionId?: string): never {\n\tthrow new DatrixAuthError(\"Session expired\", {\n\t\tcode: \"AUTH_SESSION_EXPIRED\",\n\t\tstrategy: \"session\",\n\t\tcontext: { sessionId },\n\t\tsuggestion: \"Login again to create a new session\",\n\t});\n}\n\n/**\n * Throw session delete error\n *\n * @param cause - Original error\n */\nexport function throwSessionDeleteError(cause?: Error): never {\n\tthrow new DatrixAuthError(\"Failed to delete session\", {\n\t\tcode: \"SESSION_DELETE_ERROR\",\n\t\tstrategy: \"session\",\n\t\tcause,\n\t\tsuggestion: \"Check your session store configuration\",\n\t});\n}\n\n/**\n * Throw session not configured error\n */\nexport function throwSessionNotConfigured(): never {\n\tthrow new DatrixAuthError(\"Session strategy not configured\", {\n\t\tcode: \"SESSION_NOT_CONFIGURED\",\n\t\tstrategy: \"session\",\n\t\tsuggestion: \"Add session configuration to your auth config\",\n\t\texpected: \"AuthConfig.session to be defined\",\n\t});\n}\n\n// ============================================================================\n// Password Errors\n// ============================================================================\n\n/**\n * Throw password too short error\n *\n * @param minLength - Minimum password length\n * @param actualLength - Actual password length\n */\nexport function throwPasswordTooShort(\n\tminLength: number,\n\tactualLength: number,\n): never {\n\tthrow new DatrixAuthError(\n\t\t`Password must be at least ${minLength} characters`,\n\t\t{\n\t\t\tcode: \"PASSWORD_TOO_SHORT\",\n\t\t\tstrategy: \"password\",\n\t\t\tcontext: { minLength, actualLength },\n\t\t\tsuggestion: `Use a password with at least ${minLength} characters`,\n\t\t\texpected: `password length >= ${minLength}`,\n\t\t\treceived: actualLength,\n\t\t},\n\t);\n}\n\n/**\n * Throw password hash error\n *\n * @param cause - Original error\n */\nexport function throwPasswordHashError(cause?: Error): never {\n\tthrow new DatrixAuthError(\"Failed to hash password\", {\n\t\tcode: \"PASSWORD_HASH_ERROR\",\n\t\tstrategy: \"password\",\n\t\tcause,\n\t\tsuggestion: \"Check your password hashing configuration\",\n\t});\n}\n\n/**\n * Throw password verify error\n *\n * @param cause - Original error\n */\nexport function throwPasswordVerifyError(cause?: Error): never {\n\tthrow new DatrixAuthError(\"Failed to verify password\", {\n\t\tcode: \"PASSWORD_VERIFY_ERROR\",\n\t\tstrategy: \"password\",\n\t\tcause,\n\t\tsuggestion: \"Ensure the password hash and salt are valid\",\n\t});\n}\n\n// ============================================================================\n// General Auth Errors\n// ============================================================================\n\n/**\n * Throw invalid credentials error\n */\nexport function throwInvalidCredentials(): never {\n\tthrow new DatrixAuthError(\"Invalid email or password\", {\n\t\tcode: \"AUTH_INVALID_CREDENTIALS\",\n\t\tsuggestion: \"Check your email and password and try again\",\n\t});\n}\n\n/**\n * Throw user not found error\n *\n * @param email - User email\n */\nexport function throwUserNotFound(email?: string): never {\n\tthrow new DatrixAuthError(\"User not found\", {\n\t\tcode: \"AUTH_USER_NOT_FOUND\",\n\t\tcontext: { email },\n\t\tsuggestion: email\n\t\t\t? `No user found with email: ${email}`\n\t\t\t: \"User does not exist\",\n\t});\n}\n\n/**\n * Throw user already exists error\n *\n * @param email - User email\n */\nexport function throwUserExists(email: string): never {\n\tthrow new DatrixAuthError(`User with email ${email} already exists`, {\n\t\tcode: \"AUTH_USER_EXISTS\",\n\t\tcontext: { email },\n\t\tsuggestion: \"Use a different email or try logging in\",\n\t});\n}\n\n/**\n * Throw unauthorized error\n *\n * @param message - Custom message\n */\nexport function throwUnauthorized(message = \"Authentication required\"): never {\n\tthrow new DatrixAuthError(message, {\n\t\tcode: \"AUTH_UNAUTHORIZED\",\n\t\tsuggestion: \"Provide valid authentication credentials\",\n\t});\n}\n\n/**\n * Throw forbidden error\n *\n * @param action - Action attempted\n * @param resource - Resource name\n * @param role - User role\n */\nexport function throwForbidden(\n\taction?: string,\n\tresource?: string,\n\trole?: string,\n): never {\n\tconst message =\n\t\taction && resource\n\t\t\t? `You don't have permission to ${action} ${resource}`\n\t\t\t: \"Access forbidden\";\n\n\tthrow new DatrixAuthError(message, {\n\t\tcode: \"AUTH_FORBIDDEN\",\n\t\tstrategy: \"permission\",\n\t\tcontext: { action, resource, role },\n\t\tsuggestion: \"Contact your administrator for access\",\n\t});\n}\n\n/**\n * Throw permission denied error\n *\n * @param action - Action attempted\n * @param resource - Resource name\n * @param field - Field name (optional)\n */\nexport function throwPermissionDenied(\n\taction: string,\n\tresource: string,\n\tfield?: string,\n): never {\n\tconst message = field\n\t\t? `Permission denied: Cannot ${action} field \"${field}\" on ${resource}`\n\t\t: `Permission denied: Cannot ${action} ${resource}`;\n\n\tthrow new DatrixAuthError(message, {\n\t\tcode: \"PERMISSION_DENIED\",\n\t\tstrategy: \"permission\",\n\t\tcontext: { action, resource, field },\n\t\tsuggestion: \"Check your role permissions\",\n\t});\n}\n\n/**\n * Throw auth config invalid error\n *\n * @param message - Error message\n * @param field - Config field name\n */\nexport function throwAuthConfigInvalid(message: string, field?: string): never {\n\tthrow new DatrixAuthError(message, {\n\t\tcode: \"AUTH_CONFIG_INVALID\",\n\t\tcontext: { field },\n\t\tsuggestion: \"Check your authentication configuration\",\n\t});\n}\n","/**\n * JWT Strategy\n *\n * Implements JWT token signing and verification using Node.js crypto module.\n * No external dependencies (no jsonwebtoken library).\n */\n\nimport { createHmac, timingSafeEqual } from \"node:crypto\";\nimport type {\n\tJwtConfig,\n\tJwtPayload,\n\tJwtHeader,\n\tJwtAlgorithm,\n\tTimeUnit,\n\tExpiryString,\n} from \"./types\";\nimport { DEFAULT_API_AUTH_CONFIG } from \"@datrix/core\";\nimport { isJwtPayload } from \"./types\";\nimport {\n\tthrowJwtSignError,\n\tthrowJwtVerifyError,\n\tthrowJwtDecodeError,\n\tthrowJwtInvalidFormat,\n\tthrowJwtInvalidHeader,\n\tthrowJwtInvalidPayload,\n\tthrowJwtInvalidSignature,\n\tthrowJwtExpired,\n\tthrowJwtInvalidIat,\n\tthrowJwtInvalidIssuer,\n\tthrowJwtInvalidAudience,\n} from \"./error-helper\";\n\n/**\n * JWT Strategy\n *\n * Handles JWT token creation and verification\n */\nexport class JwtStrategy {\n\tprivate readonly secret: string;\n\tprivate readonly expiresIn: number; // in seconds\n\tprivate readonly algorithm: JwtAlgorithm;\n\tprivate readonly issuer: string | undefined;\n\tprivate readonly audience: string | undefined;\n\n\tconstructor(config: JwtConfig) {\n\t\tthis.secret = config.secret;\n\t\tthis.expiresIn = this.parseExpiry(\n\t\t\tconfig.expiresIn ?? DEFAULT_API_AUTH_CONFIG.jwt.expiresIn,\n\t\t);\n\t\tthis.algorithm = config.algorithm ?? \"HS256\";\n\t\tthis.issuer = config.issuer;\n\t\tthis.audience = config.audience;\n\t}\n\n\t/**\n\t * Sign a JWT token\n\t */\n\tsign(payload: Omit<JwtPayload, \"iat\" | \"exp\" | \"iss\" | \"aud\">): string {\n\t\ttry {\n\t\t\tconst now = Math.floor(Date.now() / 1000);\n\t\t\tconst exp = now + this.expiresIn;\n\n\t\t\tconst basePayload = payload as Record<string, unknown>;\n\t\t\tconst fullPayload: JwtPayload = {\n\t\t\t\tuserId: basePayload[\"userId\"] as number,\n\t\t\t\trole: basePayload[\"role\"] as string,\n\t\t\t\tiat: now,\n\t\t\t\texp,\n\t\t\t\t...(this.issuer && { iss: this.issuer }),\n\t\t\t\t...(this.audience && { aud: this.audience }),\n\t\t\t\t...basePayload,\n\t\t\t};\n\n\t\t\tconst token = this.createToken(fullPayload);\n\n\t\t\treturn token;\n\t\t} catch (error) {\n\t\t\tthrowJwtSignError(error instanceof Error ? error : undefined);\n\t\t}\n\t}\n\n\t/**\n\t * Verify a JWT token\n\t */\n\tverify(token: string): JwtPayload {\n\t\ttry {\n\t\t\t// Split token into parts\n\t\t\tconst parts = token.split(\".\");\n\t\t\tif (parts.length !== 3) {\n\t\t\t\tthrowJwtInvalidFormat();\n\t\t\t}\n\n\t\t\tconst encodedHeader = parts[0];\n\t\t\tconst encodedPayload = parts[1];\n\t\t\tconst signature = parts[2];\n\n\t\t\tif (!encodedHeader || !encodedPayload || !signature) {\n\t\t\t\tthrowJwtInvalidFormat();\n\t\t\t}\n\n\t\t\t// Verify signature\n\t\t\tconst expectedSignature = this.signData(\n\t\t\t\t`${encodedHeader}.${encodedPayload}`,\n\t\t\t);\n\n\t\t\tif (!this.constantTimeCompare(signature, expectedSignature)) {\n\t\t\t\tthrowJwtInvalidSignature();\n\t\t\t}\n\n\t\t\t// Decode and validate header\n\t\t\tconst header = this.decodeBase64Url<JwtHeader>(encodedHeader);\n\t\t\tif (!header || header.typ !== \"JWT\" || header.alg !== this.algorithm) {\n\t\t\t\tthrowJwtInvalidHeader();\n\t\t\t}\n\n\t\t\t// Decode and validate payload\n\t\t\tconst payload = this.decodeBase64Url<JwtPayload>(encodedPayload);\n\t\t\tif (!payload || !isJwtPayload(payload)) {\n\t\t\t\tthrowJwtInvalidPayload();\n\t\t\t}\n\n\t\t\t// Check expiration\n\t\t\tconst now = Math.floor(Date.now() / 1000);\n\t\t\tif (payload.exp < now) {\n\t\t\t\tthrowJwtExpired(payload.exp, now);\n\t\t\t}\n\n\t\t\t// Check issued at (not in future)\n\t\t\tif (payload.iat > now + 60) {\n\t\t\t\t// Allow 60s clock skew\n\t\t\t\tthrowJwtInvalidIat();\n\t\t\t}\n\n\t\t\t// Check issuer if configured\n\t\t\tif (this.issuer !== undefined && payload.iss !== this.issuer) {\n\t\t\t\tthrowJwtInvalidIssuer(this.issuer, payload.iss);\n\t\t\t}\n\n\t\t\t// Check audience if configured\n\t\t\tif (this.audience !== undefined && payload.aud !== this.audience) {\n\t\t\t\tthrowJwtInvalidAudience(this.audience, payload.aud);\n\t\t\t}\n\n\t\t\treturn payload;\n\t\t} catch (error) {\n\t\t\tif (error instanceof Error && error.name === \"DatrixAuthError\") {\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t\tthrowJwtVerifyError(error instanceof Error ? error : undefined);\n\t\t}\n\t}\n\n\t/**\n\t * Refresh a JWT token\n\t *\n\t * Creates a new token with updated expiration\n\t */\n\trefresh(token: string): string {\n\t\tconst payload = this.verify(token);\n\n\t\tconst { userId, role, ...rest } = payload;\n\n\t\t// Remove standard claims\n\t\tconst { iat: _iat, exp: _exp, iss: _iss, aud: _aud, ...custom } = rest;\n\n\t\treturn this.sign({ userId, role, ...custom });\n\t}\n\n\t/**\n\t * Decode token without verification (for debugging)\n\t */\n\tdecode(token: string): JwtPayload {\n\t\ttry {\n\t\t\tconst parts = token.split(\".\");\n\t\t\tif (parts.length !== 3) {\n\t\t\t\tthrowJwtInvalidFormat();\n\t\t\t}\n\n\t\t\tconst encodedPayload = parts[1];\n\t\t\tif (!encodedPayload) {\n\t\t\t\tthrowJwtInvalidFormat();\n\t\t\t}\n\n\t\t\tconst payload = this.decodeBase64Url<JwtPayload>(encodedPayload);\n\t\t\tif (!payload || !isJwtPayload(payload)) {\n\t\t\t\tthrowJwtInvalidPayload();\n\t\t\t}\n\n\t\t\treturn payload;\n\t\t} catch (error) {\n\t\t\tif (error instanceof Error && error.name === \"DatrixAuthError\") {\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t\tthrowJwtDecodeError(error instanceof Error ? error : undefined);\n\t\t}\n\t}\n\n\t/**\n\t * Create a JWT token from payload\n\t */\n\tprivate createToken(payload: JwtPayload): string {\n\t\tconst header: JwtHeader = {\n\t\t\talg: this.algorithm,\n\t\t\ttyp: \"JWT\",\n\t\t};\n\n\t\tconst encodedHeader = this.encodeBase64Url(JSON.stringify(header));\n\t\tconst encodedPayload = this.encodeBase64Url(JSON.stringify(payload));\n\n\t\tconst signature = this.signData(`${encodedHeader}.${encodedPayload}`);\n\n\t\treturn `${encodedHeader}.${encodedPayload}.${signature}`;\n\t}\n\n\t/**\n\t * Sign data using HMAC\n\t */\n\tprivate signData(data: string): string {\n\t\tconst algorithm = this.algorithm === \"HS256\" ? \"sha256\" : \"sha512\";\n\t\tconst hmac = createHmac(algorithm, this.secret);\n\t\thmac.update(data);\n\t\treturn this.encodeBase64Url(hmac.digest(\"base64\"));\n\t}\n\n\t/**\n\t * Base64 URL encode\n\t */\n\tprivate encodeBase64Url(str: string): string {\n\t\treturn Buffer.from(str)\n\t\t\t.toString(\"base64\")\n\t\t\t.replace(/\\+/g, \"-\")\n\t\t\t.replace(/\\//g, \"_\")\n\t\t\t.replace(/=/g, \"\");\n\t}\n\n\t/**\n\t * Base64 URL decode\n\t */\n\tprivate decodeBase64Url<T>(str: string): T | undefined {\n\t\ttry {\n\t\t\t// Add padding if needed\n\t\t\tlet padded = str;\n\t\t\twhile (padded.length % 4 !== 0) {\n\t\t\t\tpadded += \"=\";\n\t\t\t}\n\n\t\t\t// Replace URL-safe characters\n\t\t\tconst base64 = padded.replace(/-/g, \"+\").replace(/_/g, \"/\");\n\n\t\t\tconst decoded = Buffer.from(base64, \"base64\").toString(\"utf8\");\n\t\t\treturn JSON.parse(decoded) as T;\n\t\t} catch {\n\t\t\treturn undefined;\n\t\t}\n\t}\n\n\t/**\n\t * Constant-time string comparison (prevent timing attacks)\n\t */\n\tprivate constantTimeCompare(a: string, b: string): boolean {\n\t\tif (a.length !== b.length) {\n\t\t\treturn false;\n\t\t}\n\n\t\ttry {\n\t\t\tconst bufferA = Buffer.from(a);\n\t\t\tconst bufferB = Buffer.from(b);\n\t\t\treturn timingSafeEqual(bufferA, bufferB);\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t/**\n\t * Parse expiry string or number to seconds\n\t */\n\tprivate parseExpiry(expiry: ExpiryString | number): number {\n\t\tif (typeof expiry === \"number\") {\n\t\t\treturn expiry;\n\t\t}\n\n\t\tconst match = expiry.match(/^(\\d+)([smhd])$/);\n\t\tif (!match) {\n\t\t\treturn 3600; // default 1 hour\n\t\t}\n\n\t\tconst [, num, unit] = match as [string, string, TimeUnit];\n\t\tconst value = parseInt(num, 10);\n\n\t\tconst multipliers: Record<TimeUnit, number> = {\n\t\t\ts: 1,\n\t\t\tm: 60,\n\t\t\th: 3600,\n\t\t\td: 86400,\n\t\t};\n\n\t\treturn value * multipliers[unit];\n\t}\n}\n\n/**\n * Create a JWT strategy instance\n */\nexport function createJwtStrategy(config: JwtConfig): JwtStrategy {\n\treturn new JwtStrategy(config);\n}\n","/**\n * Authentication Plugin Types\n *\n * Type definitions for the authentication plugin including JWT and Session.\n * Permission/RBAC types are now in @datrix/core.\n */\n\nimport type { DefaultPermission } from \"@datrix/core\";\nimport type { PasswordConfig } from \"./password\";\n\n/**\n * JWT payload (base)\n */\nexport interface JwtPayload {\n\treadonly userId: number;\n\treadonly role: string;\n\treadonly iat: number;\n\treadonly exp: number;\n\treadonly iss?: string;\n\treadonly aud?: string;\n\treadonly [key: string]: unknown;\n}\n\n/**\n * Session store interface\n *\n * Implement this to use a custom session backend (Redis, database, etc.)\n */\nexport interface SessionStore {\n\tget(sessionId: string): Promise<SessionData | undefined>;\n\tset(sessionId: string, data: SessionData): Promise<void>;\n\tdelete(sessionId: string): Promise<void>;\n\tcleanup(): Promise<number>;\n\tclear(): Promise<void>;\n}\n\n/**\n * Session data\n */\nexport interface SessionData {\n\treadonly id: string;\n\treadonly userId: number;\n\treadonly role: string;\n\treadonly createdAt: Date;\n\treadonly expiresAt: Date;\n\treadonly lastAccessedAt: Date;\n\treadonly [key: string]: unknown;\n}\n\n/**\n * JWT algorithm types\n */\nexport type JwtAlgorithm = \"HS256\" | \"HS512\";\n\n/**\n * Time unit for expiration\n */\nexport type TimeUnit = \"s\" | \"m\" | \"h\" | \"d\";\n\n/**\n * Expiry string format (e.g., \"1h\", \"7d\", \"30m\")\n */\nexport type ExpiryString = `${number}${TimeUnit}`;\n\n/**\n * JWT configuration\n */\nexport interface JwtConfig {\n\treadonly secret: string;\n\treadonly expiresIn?: ExpiryString | number; // String like \"1h\" or seconds as number\n\treadonly algorithm?: JwtAlgorithm;\n\treadonly issuer?: string;\n\treadonly audience?: string;\n}\n\n/**\n * JWT token parts\n */\nexport interface JwtToken {\n\treadonly header: JwtHeader;\n\treadonly payload: JwtPayload;\n\treadonly signature: string;\n}\n\n/**\n * JWT header\n */\nexport interface JwtHeader {\n\treadonly alg: JwtAlgorithm;\n\treadonly typ: \"JWT\";\n}\n\n/**\n * Session configuration\n */\nexport interface SessionConfig {\n\t/**\n\t * Session storage backend.\n\t * Pass a custom SessionStore implementation for Redis, database, etc.\n\t * Defaults to in-memory store.\n\t */\n\treadonly store?: \"memory\" | SessionStore;\n\treadonly maxAge?: number; // seconds\n\treadonly checkPeriod?: number; // cleanup interval in seconds\n\treadonly prefix?: string; // session key prefix (only used with default memory store)\n}\n\n/**\n * Authentication configuration\n *\n * When auth is defined in ApiConfig, authentication is enabled.\n * When auth is undefined, authentication is disabled.\n *\n * @template TRole - Union type of valid role names\n */\nexport interface AuthConfig<TRole extends string = string> {\n\t/**\n\t * Available roles in the application\n\t */\n\treadonly roles: readonly TRole[];\n\n\t/**\n\t * Default role for new users\n\t */\n\treadonly defaultRole: TRole;\n\n\t/**\n\t * Default permission applied to schemas without explicit permissions\n\t */\n\treadonly defaultPermission?: DefaultPermission<TRole>;\n\n\t/**\n\t * JWT configuration (required if not using session)\n\t */\n\treadonly jwt?: JwtConfig;\n\n\t/**\n\t * Session configuration (required if not using JWT)\n\t */\n\treadonly session?: SessionConfig;\n\n\t/**\n\t * Password hashing configuration (PBKDF2)\n\t * @default { iterations: 100000, keyLength: 64, minLength: 8 }\n\t */\n\treadonly password?: PasswordConfig;\n\n\t/**\n\t * Name for the authentication schema/table\n\t * @default 'authentication'\n\t */\n\treadonly authSchemaName?: string;\n\n\t/**\n\t * User schema configuration\n\t */\n\treadonly userSchema?: {\n\t\t/** User schema name @default 'user' */\n\t\treadonly name?: string;\n\t\t/** Email field name @default 'email' */\n\t\treadonly email?: string;\n\t};\n\n\t/**\n\t * Auth endpoint configuration\n\t */\n\treadonly endpoints?: {\n\t\treadonly login?: string;\n\t\treadonly register?: string;\n\t\treadonly logout?: string;\n\t\treadonly me?: string;\n\t\treadonly disableRegister?: boolean;\n\t};\n}\n\n/**\n * @deprecated Use AuthConfig instead\n */\nexport type AuthPluginOptions = AuthConfig<string>;\n\n/**\n * Login credentials\n */\nexport interface LoginCredentials {\n\treadonly email: string;\n\treadonly password: string;\n}\n\n/**\n * Password hash result\n */\nexport interface PasswordHash {\n\treadonly hash: string;\n\treadonly salt: string;\n}\n\n/**\n * Type guard for JWT payload\n */\nexport function isJwtPayload(value: unknown): value is JwtPayload {\n\tif (typeof value !== \"object\" || value === null) {\n\t\treturn false;\n\t}\n\n\tconst obj = value as Record<string, unknown>;\n\n\treturn (\n\t\t\"userId\" in obj &&\n\t\t\"role\" in obj &&\n\t\t\"iat\" in obj &&\n\t\t\"exp\" in obj &&\n\t\ttypeof obj[\"userId\"] === \"number\" &&\n\t\ttypeof obj[\"role\"] === \"string\" &&\n\t\ttypeof obj[\"iat\"] === \"number\" &&\n\t\ttypeof obj[\"exp\"] === \"number\"\n\t);\n}\n\n/**\n * Type guard for session data\n */\nexport function isSessionData(value: unknown): value is SessionData {\n\tif (typeof value !== \"object\" || value === null) {\n\t\treturn false;\n\t}\n\n\tconst obj = value as Record<string, unknown>;\n\n\treturn (\n\t\t\"id\" in obj &&\n\t\t\"userId\" in obj &&\n\t\t\"role\" in obj &&\n\t\t\"createdAt\" in obj &&\n\t\t\"expiresAt\" in obj &&\n\t\t\"lastAccessedAt\" in obj &&\n\t\ttypeof obj[\"id\"] === \"string\" &&\n\t\ttypeof obj[\"userId\"] === \"string\" &&\n\t\ttypeof obj[\"role\"] === \"string\" &&\n\t\tobj[\"createdAt\"] instanceof Date &&\n\t\tobj[\"expiresAt\"] instanceof Date &&\n\t\tobj[\"lastAccessedAt\"] instanceof Date\n\t);\n}\n\n/**\n * Minimum JWT secret length (256 bits = 32 characters for HS256)\n */\nconst MIN_JWT_SECRET_LENGTH = 32;\n\n/**\n * Type guard for auth config\n */\nexport function isAuthConfig(value: unknown): value is AuthConfig<string> {\n\tif (typeof value !== \"object\" || value === null) {\n\t\treturn false;\n\t}\n\n\tconst opts = value as Record<string, unknown>;\n\n\t// roles and defaultRole are required\n\tif (!(\"roles\" in opts) || !Array.isArray(opts[\"roles\"])) {\n\t\treturn false;\n\t}\n\n\tif (!(\"defaultRole\" in opts) || typeof opts[\"defaultRole\"] !== \"string\") {\n\t\treturn false;\n\t}\n\n\t// At least one auth strategy must be configured\n\tif (!(\"jwt\" in opts) && !(\"session\" in opts)) {\n\t\treturn false;\n\t}\n\n\t// Validate JWT config if present\n\tif (\"jwt\" in opts && opts[\"jwt\"] !== undefined) {\n\t\tif (typeof opts[\"jwt\"] !== \"object\" || opts[\"jwt\"] === null) {\n\t\t\treturn false;\n\t\t}\n\t\tconst jwt = opts[\"jwt\"] as Record<string, unknown>;\n\t\tif (\n\t\t\t!(\"secret\" in jwt) ||\n\t\t\ttypeof jwt[\"secret\"] !== \"string\" ||\n\t\t\tjwt[\"secret\"].length < MIN_JWT_SECRET_LENGTH\n\t\t) {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\treturn true;\n}\n\n/**\n * @deprecated Use isAuthConfig instead\n */\nexport const isAuthPluginOptions = isAuthConfig;\n","/**\n * Session Strategy\n *\n * Implements session management with pluggable storage backends.\n * Includes in-memory store by default.\n */\n\nimport { randomBytes } from \"node:crypto\";\nimport type { SessionConfig, SessionData, SessionStore } from \"./types\";\nimport { DEFAULT_API_AUTH_CONFIG } from \"@datrix/core\";\nimport {\n\tthrowSessionCreateError,\n\tthrowSessionNotFound,\n\tthrowSessionExpired,\n} from \"./error-helper\";\nimport { DatrixAuthError } from \"@datrix/core\";\n\n/**\n * Session Strategy\n *\n * Manages user sessions with configurable storage\n */\nexport class SessionStrategy {\n\tprivate readonly store: SessionStore;\n\tprivate readonly maxAge: number; // in seconds\n\tprivate readonly checkPeriod: number; // cleanup interval in seconds\n\tprivate cleanupTimer: NodeJS.Timeout | undefined;\n\n\tconstructor(config: SessionConfig) {\n\t\tthis.maxAge = config.maxAge ?? 86400; // default 24 hours\n\t\tthis.checkPeriod = config.checkPeriod ?? 3600; // default 1 hour\n\n\t\tif (config.store && typeof config.store !== \"string\") {\n\t\t\tthis.store = config.store;\n\t\t} else {\n\t\t\tthis.store = new MemorySessionStore(config.prefix);\n\t\t}\n\t}\n\n\t/**\n\t * Create a new session\n\t */\n\tasync create(\n\t\tuserId: number,\n\t\trole: string,\n\t\tdata?: Record<string, unknown>,\n\t): Promise<SessionData> {\n\t\ttry {\n\t\t\tconst sessionId = this.generateSessionId();\n\t\t\tconst now = new Date();\n\t\t\tconst expiresAt = new Date(now.getTime() + this.maxAge * 1000);\n\n\t\t\tconst sessionData: SessionData = {\n\t\t\t\tid: sessionId,\n\t\t\t\tuserId,\n\t\t\t\trole,\n\t\t\t\tcreatedAt: now,\n\t\t\t\texpiresAt,\n\t\t\t\tlastAccessedAt: now,\n\t\t\t\t...data,\n\t\t\t};\n\n\t\t\tawait this.store.set(sessionId, sessionData);\n\n\t\t\treturn sessionData;\n\t\t} catch (error) {\n\t\t\tif (error instanceof Error && error.name === \"DatrixAuthError\") {\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t\tthrowSessionCreateError(error instanceof Error ? error : undefined);\n\t\t}\n\t}\n\n\t/**\n\t * Get session by ID\n\t */\n\tasync get(sessionId: string): Promise<SessionData> {\n\t\tconst session = await this.store.get(sessionId);\n\n\t\tif (session === undefined) {\n\t\t\tthrowSessionNotFound(sessionId);\n\t\t}\n\n\t\t// Check if session expired\n\t\tconst now = new Date();\n\t\tif (session.expiresAt < now) {\n\t\t\t// Delete expired session\n\t\t\tawait this.store.delete(sessionId);\n\t\t\tthrowSessionExpired(sessionId);\n\t\t}\n\n\t\t// Update last accessed time\n\t\tconst updatedSession: SessionData = {\n\t\t\t...session,\n\t\t\tlastAccessedAt: now,\n\t\t};\n\n\t\tawait this.store.set(sessionId, updatedSession);\n\n\t\treturn updatedSession;\n\t}\n\n\t/**\n\t * Update session data\n\t */\n\tasync update(\n\t\tsessionId: string,\n\t\tdata: Partial<Omit<SessionData, \"id\" | \"createdAt\">>,\n\t): Promise<SessionData> {\n\t\tconst session = await this.get(sessionId);\n\n\t\tconst updatedSession: SessionData = {\n\t\t\t...session,\n\t\t\t...data,\n\t\t\tid: session.id, // Preserve ID\n\t\t\tcreatedAt: session.createdAt, // Preserve creation time\n\t\t};\n\n\t\tawait this.store.set(sessionId, updatedSession);\n\n\t\treturn updatedSession;\n\t}\n\n\t/**\n\t * Delete session\n\t */\n\tasync delete(sessionId: string): Promise<void> {\n\t\tawait this.store.delete(sessionId);\n\t}\n\n\t/**\n\t * Refresh session (extend expiration)\n\t */\n\tasync refresh(sessionId: string): Promise<SessionData> {\n\t\tconst now = new Date();\n\t\tconst expiresAt = new Date(now.getTime() + this.maxAge * 1000);\n\n\t\treturn this.update(sessionId, { expiresAt, lastAccessedAt: now });\n\t}\n\n\t/**\n\t * Validate session (check if exists and not expired)\n\t */\n\tasync validate(sessionId: string): Promise<boolean> {\n\t\ttry {\n\t\t\tawait this.get(sessionId);\n\t\t\treturn true;\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t/**\n\t * Start cleanup timer\n\t */\n\tstartCleanup(): void {\n\t\tif (this.cleanupTimer !== undefined) {\n\t\t\treturn;\n\t\t}\n\n\t\tthis.cleanupTimer = setInterval(() => {\n\t\t\tvoid this.store.cleanup();\n\t\t}, this.checkPeriod * 1000);\n\n\t\t// Don't prevent Node.js from exiting\n\t\tthis.cleanupTimer.unref();\n\t}\n\n\t/**\n\t * Stop cleanup timer\n\t */\n\tstopCleanup(): void {\n\t\tif (this.cleanupTimer !== undefined) {\n\t\t\tclearInterval(this.cleanupTimer);\n\t\t\tthis.cleanupTimer = undefined;\n\t\t}\n\t}\n\n\t/**\n\t * Clear all sessions\n\t */\n\tasync clear(): Promise<void> {\n\t\tawait this.store.clear();\n\t}\n\n\t/**\n\t * Generate secure session ID\n\t */\n\tprivate generateSessionId(): string {\n\t\treturn randomBytes(32).toString(\"hex\");\n\t}\n}\n\n/**\n * In-Memory Session Store\n *\n * Default session store implementation using Map\n */\nexport class MemorySessionStore implements SessionStore {\n\treadonly name = \"memory\" as const;\n\tprivate readonly sessions: Map<string, SessionData> = new Map();\n\tprivate readonly prefix: string;\n\n\tconstructor(prefix: string = DEFAULT_API_AUTH_CONFIG.session.prefix) {\n\t\tthis.prefix = prefix;\n\t}\n\n\tasync get(sessionId: string): Promise<SessionData | undefined> {\n\t\ttry {\n\t\t\tconst key = this.getKey(sessionId);\n\t\t\tconst session = this.sessions.get(key);\n\n\t\t\treturn session;\n\t\t} catch (error) {\n\t\t\tthrow new DatrixAuthError(\"Failed to get session from store\", {\n\t\t\t\tcode: \"SESSION_CREATE_ERROR\",\n\t\t\t\tstrategy: \"session\",\n\t\t\t\tcause: error instanceof Error ? error : undefined,\n\t\t\t});\n\t\t}\n\t}\n\n\tasync set(sessionId: string, data: SessionData): Promise<void> {\n\t\tconst key = this.getKey(sessionId);\n\t\tthis.sessions.set(key, data);\n\t}\n\n\tasync delete(sessionId: string): Promise<void> {\n\t\tconst key = this.getKey(sessionId);\n\t\tthis.sessions.delete(key);\n\t}\n\n\tasync cleanup(): Promise<number> {\n\t\tconst now = new Date();\n\t\tlet deletedCount = 0;\n\n\t\tfor (const [key, session] of this.sessions.entries()) {\n\t\t\tif (session.expiresAt < now) {\n\t\t\t\tthis.sessions.delete(key);\n\t\t\t\tdeletedCount++;\n\t\t\t}\n\t\t}\n\n\t\treturn deletedCount;\n\t}\n\n\tasync clear(): Promise<void> {\n\t\tthis.sessions.clear();\n\t}\n\n\tprivate getKey(sessionId: string): string {\n\t\treturn `${this.prefix}${sessionId}`;\n\t}\n}\n","/**\n * Auth Manager\n *\n * Central authentication manager for API package.\n * Integrates password hashing, JWT, and sessions.\n * Note: Permission checking is now handled by schema-based permissions in middleware/permission.ts\n */\n\nimport { PasswordManager, type PasswordHash } from \"./password\";\nimport { JwtStrategy } from \"./jwt\";\nimport { SessionStrategy } from \"./session\";\nimport { AuthConfig } from \"./types\";\nimport { throwSessionNotConfigured } from \"./error-helper\";\nimport { AuthContext, AuthUser, IAuthManager, LoginResult } from \"@datrix/core\";\n\n/**\n * Auth Manager\n *\n * Main authentication manager that coordinates all auth components.\n * Note: Permission checking is now schema-based (see middleware/permission.ts)\n */\nexport class AuthManager<\n\tTRole extends string = string,\n> implements IAuthManager {\n\tprivate readonly passwordManager: PasswordManager;\n\tprivate readonly jwtStrategy: JwtStrategy | undefined;\n\tprivate readonly sessionStrategy: SessionStrategy | undefined;\n\tprivate readonly config: AuthConfig<TRole>;\n\n\tpublic get authConfig(): AuthConfig<TRole> {\n\t\treturn this.config;\n\t}\n\n\tconstructor(config: AuthConfig<TRole>) {\n\t\tthis.config = config;\n\n\t\t// Initialize password manager\n\t\tthis.passwordManager = new PasswordManager(config.password);\n\n\t\t// Initialize JWT strategy if configured\n\t\tif (config.jwt) {\n\t\t\tthis.jwtStrategy = new JwtStrategy(config.jwt);\n\t\t}\n\n\t\t// Initialize session strategy if configured\n\t\tif (config.session) {\n\t\t\tthis.sessionStrategy = new SessionStrategy(config.session);\n\t\t\tthis.sessionStrategy.startCleanup();\n\t\t}\n\t}\n\n\t/**\n\t * Hash password\n\t */\n\tasync hashPassword(password: string): Promise<PasswordHash> {\n\t\treturn this.passwordManager.hash(password);\n\t}\n\n\t/**\n\t * Verify password\n\t */\n\tasync verifyPassword(\n\t\tpassword: string,\n\t\thash: string,\n\t\tsalt: string,\n\t): Promise<boolean> {\n\t\treturn this.passwordManager.verify(password, hash, salt);\n\t}\n\n\t/**\n\t * Login user and create token/session\n\t */\n\tasync login(\n\t\tuser: AuthUser,\n\t\toptions: { createToken?: boolean; createSession?: boolean } = {},\n\t): Promise<LoginResult> {\n\t\tconst { createToken = true, createSession = true } = options;\n\n\t\tlet token: string | undefined = undefined;\n\t\tlet sessionId: string | undefined = undefined;\n\n\t\t// Create JWT token if enabled and requested\n\t\tif (this.jwtStrategy && createToken) {\n\t\t\ttoken = this.jwtStrategy.sign({\n\t\t\t\tuserId: user.id,\n\t\t\t\trole: user.role,\n\t\t\t});\n\t\t}\n\n\t\t// Create session if enabled and requested\n\t\tif (this.sessionStrategy && createSession) {\n\t\t\tconst sessionData = await this.sessionStrategy.create(user.id, user.role);\n\t\t\tsessionId = sessionData.id;\n\t\t}\n\n\t\tconst result: LoginResult = {\n\t\t\tuser,\n\t\t\t...(token !== undefined && { token }),\n\t\t\t...(sessionId !== undefined && { sessionId }),\n\t\t};\n\n\t\treturn result;\n\t}\n\n\t/**\n\t * Logout user (destroy session)\n\t */\n\tasync logout(sessionId: string): Promise<void> {\n\t\tif (!this.sessionStrategy) {\n\t\t\tthrowSessionNotConfigured();\n\t\t}\n\n\t\tawait this.sessionStrategy.delete(sessionId);\n\t}\n\n\t/**\n\t * Authenticate request (extract and verify token/session)\n\t */\n\tasync authenticate(request: Request): Promise<AuthContext | null> {\n\t\t// Try JWT first\n\t\tconst token = this.extractToken(request);\n\t\tif (token && this.jwtStrategy) {\n\t\t\ttry {\n\t\t\t\tconst payload = this.jwtStrategy.verify(token);\n\t\t\t\treturn {\n\t\t\t\t\tuser: {\n\t\t\t\t\t\tid: payload.userId,\n\t\t\t\t\t\temail: \"\", // Will be fetched from DB if needed\n\t\t\t\t\t\trole: payload.role,\n\t\t\t\t\t},\n\t\t\t\t\ttoken,\n\t\t\t\t};\n\t\t\t} catch {\n\t\t\t\t// JWT verification failed, continue to session\n\t\t\t}\n\t\t}\n\n\t\t// Try session\n\t\tconst sessionId = this.extractSessionId(request);\n\t\tif (sessionId && this.sessionStrategy) {\n\t\t\ttry {\n\t\t\t\tconst session = await this.sessionStrategy.get(sessionId);\n\t\t\t\treturn {\n\t\t\t\t\tuser: {\n\t\t\t\t\t\tid: session.userId,\n\t\t\t\t\t\temail: \"\",\n\t\t\t\t\t\trole: session.role,\n\t\t\t\t\t},\n\t\t\t\t\tsessionId,\n\t\t\t\t};\n\t\t\t} catch {\n\t\t\t\t// Session not found or expired\n\t\t\t}\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t/**\n\t * Extract JWT token from request headers\n\t */\n\tprivate extractToken(request: Request): string | null {\n\t\tconst authHeader = request.headers.get(\"authorization\");\n\t\tif (!authHeader || !authHeader.startsWith(\"Bearer \")) {\n\t\t\treturn null;\n\t\t}\n\n\t\treturn authHeader.slice(7); // Remove 'Bearer ' prefix\n\t}\n\n\t/**\n\t * Extract session ID from request cookies\n\t */\n\tprivate extractSessionId(request: Request): string | null {\n\t\tconst cookieHeader = request.headers.get(\"cookie\");\n\t\tif (!cookieHeader) {\n\t\t\treturn null;\n\t\t}\n\n\t\t// Parse cookies\n\t\tconst cookies = cookieHeader.split(\";\").reduce(\n\t\t\t(acc, cookie) => {\n\t\t\t\tconst [key, value] = cookie.trim().split(\"=\");\n\t\t\t\tif (key && value) {\n\t\t\t\t\tacc[key] = value;\n\t\t\t\t}\n\t\t\t\treturn acc;\n\t\t\t},\n\t\t\t{} as Record<string, string>,\n\t\t);\n\n\t\treturn cookies[\"sessionId\"] ?? null;\n\t}\n\n\t/**\n\t * Get JWT strategy (for advanced usage)\n\t */\n\tgetJwtStrategy(): JwtStrategy | undefined {\n\t\treturn this.jwtStrategy;\n\t}\n\n\t/**\n\t * Get session strategy (for advanced usage)\n\t */\n\tgetSessionStrategy(): SessionStrategy | undefined {\n\t\treturn this.sessionStrategy;\n\t}\n\n\t/**\n\t * Cleanup resources\n\t */\n\tasync destroy(): Promise<void> {\n\t\t// Stop session cleanup timer\n\t\tif (this.sessionStrategy) {\n\t\t\tthis.sessionStrategy.stopCleanup();\n\t\t}\n\n\t\t// Clear sessions\n\t\tif (this.sessionStrategy) {\n\t\t\tawait this.sessionStrategy.clear();\n\t\t}\n\t}\n}\n","/**\n * Auth Handlers\n *\n * HTTP handlers for authentication endpoints:\n * - POST /auth/register - Register new user\n * - POST /auth/login - Login user\n * - POST /auth/logout - Logout user\n * - GET /auth/me - Get current user\n *\n * Authentication data is stored in the 'authentication' table,\n * separate from user business data in the 'user' table.\n */\n\nimport type { Datrix } from \"@datrix/core\";\nimport { DEFAULT_API_AUTH_CONFIG } from \"@datrix/core\";\nimport { AuthManager } from \"../auth/manager\";\nimport type { AuthConfig } from \"../auth/types\";\nimport { jsonResponse, extractSessionId, datrixErrorResponse } from \"./utils\";\nimport { authError } from \"../errors/auth-error\";\nimport { handlerError } from \"../errors/api-error\";\nimport { DatrixError } from \"@datrix/core\";\nimport { AuthenticatedUser } from \"@datrix/core\";\nimport { DatrixEntry } from \"@datrix/core\";\nimport { AuthUser } from \"@datrix/core\";\nimport { FallbackValue } from \"@datrix/core\";\nimport { FallbackInput } from \"@datrix/core\";\n\n/**\n * Auth Handler Configuration\n */\nexport interface AuthHandlerConfig {\n\treadonly datrix: Datrix;\n\treadonly authManager: AuthManager;\n\treadonly authConfig: AuthConfig;\n}\n\n/**\n * Auth Handlers Factory\n *\n * Creates authentication endpoint handlers\n */\nexport function createAuthHandlers(config: AuthHandlerConfig) {\n\tconst { datrix, authManager, authConfig } = config;\n\n\tconst userSchemaName = authConfig.userSchema?.name ?? \"user\";\n\tconst authSchemaName = authConfig.authSchemaName ?? \"authentication\";\n\tconst userEmailField = authConfig.userSchema?.email ?? \"email\";\n\tconst defaultRole = authConfig.defaultRole;\n\n\t/**\n\t * POST /auth/register - Register new user\n\t */\n\tasync function register(request: Request): Promise<Response> {\n\t\ttry {\n\t\t\tif (authConfig.endpoints?.disableRegister) {\n\t\t\t\tthrow handlerError.permissionDenied(\"Registration is disabled\");\n\t\t\t}\n\n\t\t\tconst body = (await request.json()) as FallbackValue;\n\t\t\tconst { email, password, ...extraData } = body;\n\n\t\t\tif (!email || typeof email !== \"string\") {\n\t\t\t\tthrow handlerError.invalidBody(\"Email is required\");\n\t\t\t}\n\n\t\t\tif (!password || typeof password !== \"string\") {\n\t\t\t\tthrow handlerError.invalidBody(\"Password is required\");\n\t\t\t}\n\n\t\t\tconst existingAuth = await datrix.raw.findOne<AuthenticatedUser>(\n\t\t\t\tauthSchemaName,\n\t\t\t\t{ email: email },\n\t\t\t);\n\n\t\t\tif (existingAuth) {\n\t\t\t\tthrow handlerError.conflict(\"User with this email already exists\");\n\t\t\t}\n\n\t\t\tconst { hash, salt } = await authManager.hashPassword(password);\n\n\t\t\tconst userData = {\n\t\t\t\t[userEmailField]: email,\n\t\t\t\t...extraData,\n\t\t\t} as FallbackInput;\n\n\t\t\tlet user: DatrixEntry;\n\t\t\ttry {\n\t\t\t\tconst createdUser = await datrix.raw.create(userSchemaName, userData);\n\n\t\t\t\tif (!createdUser) {\n\t\t\t\t\tthrow handlerError.internalError(\"Failed to create user record\");\n\t\t\t\t}\n\t\t\t\tuser = createdUser;\n\t\t\t} catch (error) {\n\t\t\t\tif (error instanceof DatrixError) {\n\t\t\t\t\tthrow error;\n\t\t\t\t}\n\t\t\t\tconst message =\n\t\t\t\t\terror instanceof Error ? error.message : \"Failed to create user\";\n\t\t\t\tthrow handlerError.invalidBody(message);\n\t\t\t}\n\n\t\t\tconst authData = {\n\t\t\t\tuser: { set: [{ id: user.id }] },\n\t\t\t\temail: email,\n\t\t\t\tpassword: hash,\n\t\t\t\tpasswordSalt: salt,\n\t\t\t\trole: defaultRole,\n\t\t\t};\n\n\t\t\tconst authRecord = await datrix.raw.create<AuthenticatedUser>(\n\t\t\t\tauthSchemaName,\n\t\t\t\tauthData,\n\t\t\t);\n\n\t\t\tif (!authRecord) {\n\t\t\t\tawait datrix.raw.delete(userSchemaName, user.id);\n\t\t\t\tthrow handlerError.internalError(\n\t\t\t\t\t\"Failed to create authentication record\",\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst authUser: AuthUser = {\n\t\t\t\tid: authRecord.id,\n\t\t\t\temail: authRecord.email,\n\t\t\t\trole: authRecord.role,\n\t\t\t};\n\n\t\t\tconst loginResult = await authManager.login(authUser);\n\n\t\t\tconst responseBody = {\n\t\t\t\tdata: {\n\t\t\t\t\tuser: authUser,\n\t\t\t\t\ttoken: loginResult.token,\n\t\t\t\t\tsessionId: loginResult.sessionId,\n\t\t\t\t},\n\t\t\t};\n\n\t\t\tif (loginResult.sessionId) {\n\t\t\t\treturn new Response(JSON.stringify(responseBody), {\n\t\t\t\t\tstatus: 201,\n\t\t\t\t\theaders: {\n\t\t\t\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t\t\t\t\"Set-Cookie\": `sessionId=${loginResult.sessionId}; HttpOnly; Path=/; Max-Age=86400; SameSite=Strict`,\n\t\t\t\t\t},\n\t\t\t\t});\n\t\t\t}\n\n\t\t\treturn jsonResponse(responseBody, 201);\n\t\t} catch (error) {\n\t\t\tif (error instanceof DatrixError) {\n\t\t\t\treturn datrixErrorResponse(error);\n\t\t\t}\n\t\t\tconst message =\n\t\t\t\terror instanceof Error ? error.message : \"Internal server error\";\n\t\t\treturn datrixErrorResponse(\n\t\t\t\thandlerError.internalError(\n\t\t\t\t\tmessage,\n\t\t\t\t\terror instanceof Error ? error : undefined,\n\t\t\t\t),\n\t\t\t);\n\t\t}\n\t}\n\n\t/**\n\t * POST /auth/login - Login user\n\t */\n\tasync function login(request: Request): Promise<Response> {\n\t\ttry {\n\t\t\tconst body = await request.json();\n\t\t\tconst { email, password } = body as Record<string, string>;\n\n\t\t\tif (!email || typeof email !== \"string\") {\n\t\t\t\tthrow handlerError.invalidBody(\"Email is required\");\n\t\t\t}\n\n\t\t\tif (!password || typeof password !== \"string\") {\n\t\t\t\tthrow handlerError.invalidBody(\"Password is required\");\n\t\t\t}\n\n\t\t\tconst authRecord = await datrix.raw.findOne<AuthenticatedUser>(\n\t\t\t\tauthSchemaName,\n\t\t\t\t{ email: email },\n\t\t\t);\n\n\t\t\tif (!authRecord) {\n\t\t\t\tthrow authError.invalidCredentials();\n\t\t\t}\n\n\t\t\tconst isValid = await authManager.verifyPassword(\n\t\t\t\tpassword,\n\t\t\t\tauthRecord.password,\n\t\t\t\tauthRecord.passwordSalt,\n\t\t\t);\n\n\t\t\tif (!isValid) {\n\t\t\t\tthrow authError.invalidCredentials();\n\t\t\t}\n\n\t\t\tconst authUser: AuthUser = {\n\t\t\t\tid: authRecord.id,\n\t\t\t\temail: authRecord.email,\n\t\t\t\trole: authRecord.role,\n\t\t\t};\n\n\t\t\tconst loginResult = await authManager.login(authUser);\n\n\t\t\tconst responseBody = {\n\t\t\t\tdata: {\n\t\t\t\t\tuser: authUser,\n\t\t\t\t\ttoken: loginResult.token,\n\t\t\t\t\tsessionId: loginResult.sessionId,\n\t\t\t\t},\n\t\t\t};\n\n\t\t\tif (loginResult.sessionId) {\n\t\t\t\treturn new Response(JSON.stringify(responseBody), {\n\t\t\t\t\tstatus: 200,\n\t\t\t\t\theaders: {\n\t\t\t\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t\t\t\t\"Set-Cookie\": `sessionId=${loginResult.sessionId}; HttpOnly; Path=/; Max-Age=86400; SameSite=Strict`,\n\t\t\t\t\t},\n\t\t\t\t});\n\t\t\t}\n\n\t\t\treturn jsonResponse(responseBody);\n\t\t} catch (error) {\n\t\t\tif (error instanceof DatrixError) {\n\t\t\t\treturn datrixErrorResponse(error);\n\t\t\t}\n\t\t\tconst message =\n\t\t\t\terror instanceof Error ? error.message : \"Internal server error\";\n\t\t\treturn datrixErrorResponse(\n\t\t\t\thandlerError.internalError(\n\t\t\t\t\tmessage,\n\t\t\t\t\terror instanceof Error ? error : undefined,\n\t\t\t\t),\n\t\t\t);\n\t\t}\n\t}\n\n\t/**\n\t * POST /auth/logout - Logout user\n\t */\n\tasync function logout(request: Request): Promise<Response> {\n\t\ttry {\n\t\t\tconst sessionId = extractSessionId(request);\n\n\t\t\tif (!sessionId) {\n\t\t\t\tthrow handlerError.invalidBody(\"No session found\");\n\t\t\t}\n\n\t\t\tawait authManager.logout(sessionId);\n\n\t\t\treturn new Response(JSON.stringify({ data: { success: true } }), {\n\t\t\t\tstatus: 200,\n\t\t\t\theaders: {\n\t\t\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t\t\t\"Set-Cookie\":\n\t\t\t\t\t\t\"sessionId=; HttpOnly; Path=/; Max-Age=0; Expires=Thu, 01 Jan 1970 00:00:00 GMT; SameSite=Strict\",\n\t\t\t\t},\n\t\t\t});\n\t\t} catch (error) {\n\t\t\tif (error instanceof DatrixError) {\n\t\t\t\treturn datrixErrorResponse(error);\n\t\t\t}\n\t\t\tconst message =\n\t\t\t\terror instanceof Error ? error.message : \"Internal server error\";\n\t\t\treturn datrixErrorResponse(\n\t\t\t\thandlerError.internalError(\n\t\t\t\t\tmessage,\n\t\t\t\t\terror instanceof Error ? error : undefined,\n\t\t\t\t),\n\t\t\t);\n\t\t}\n\t}\n\n\t/**\n\t * GET /auth/me - Get current user\n\t */\n\tasync function me(request: Request): Promise<Response> {\n\t\ttry {\n\t\t\tconst authContext = await authManager.authenticate(request);\n\n\t\t\tif (!authContext || !authContext.user) {\n\t\t\t\tthrow authError.invalidToken();\n\t\t\t}\n\n\t\t\tconst authenticatedUser = await datrix.raw.findById<AuthenticatedUser>(\n\t\t\t\tauthSchemaName,\n\t\t\t\tauthContext.user.id,\n\t\t\t\t{ populate: { user: \"*\" } },\n\t\t\t);\n\n\t\t\tif (!authenticatedUser) {\n\t\t\t\tthrow handlerError.recordNotFound(\n\t\t\t\t\tuserSchemaName,\n\t\t\t\t\tString(authContext.user.id),\n\t\t\t\t);\n\t\t\t}\n\n\t\t\treturn jsonResponse({ data: authenticatedUser });\n\t\t} catch (error) {\n\t\t\tif (error instanceof DatrixError) {\n\t\t\t\treturn datrixErrorResponse(error);\n\t\t\t}\n\t\t\tconst message =\n\t\t\t\terror instanceof Error ? error.message : \"Internal server error\";\n\t\t\treturn datrixErrorResponse(\n\t\t\t\thandlerError.internalError(\n\t\t\t\t\tmessage,\n\t\t\t\t\terror instanceof Error ? error : undefined,\n\t\t\t\t),\n\t\t\t);\n\t\t}\n\t}\n\n\treturn { register, login, logout, me };\n}\n\n/**\n * Create unified auth handler (handles routing internally)\n */\nexport function createUnifiedAuthHandler(\n\tconfig: AuthHandlerConfig,\n\tapiPrefix: string = \"/api\",\n) {\n\tconst handlers = createAuthHandlers(config);\n\tconst { authConfig } = config;\n\n\tconst endpoints = {\n\t\tregister:\n\t\t\tauthConfig.endpoints?.register ??\n\t\t\tDEFAULT_API_AUTH_CONFIG.endpoints.register,\n\t\tlogin:\n\t\t\tauthConfig.endpoints?.login ?? DEFAULT_API_AUTH_CONFIG.endpoints.login,\n\t\tlogout:\n\t\t\tauthConfig.endpoints?.logout ?? DEFAULT_API_AUTH_CONFIG.endpoints.logout,\n\t\tme: authConfig.endpoints?.me ?? DEFAULT_API_AUTH_CONFIG.endpoints.me,\n\t};\n\n\treturn async function authHandler(request: Request): Promise<Response> {\n\t\tconst url = new URL(request.url);\n\t\tconst path = url.pathname.slice(apiPrefix.length);\n\t\tconst method = request.method;\n\n\t\tif (path === endpoints.register && method === \"POST\") {\n\t\t\treturn handlers.register(request);\n\t\t}\n\n\t\tif (path === endpoints.login && method === \"POST\") {\n\t\t\treturn handlers.login(request);\n\t\t}\n\n\t\tif (path === endpoints.logout && method === \"POST\") {\n\t\t\treturn handlers.logout(request);\n\t\t}\n\n\t\tif (path === endpoints.me && method === \"GET\") {\n\t\t\treturn handlers.me(request);\n\t\t}\n\n\t\treturn datrixErrorResponse(\n\t\t\thandlerError.recordNotFound(\"Auth Route\", url.pathname),\n\t\t);\n\t};\n}\n","/**\n * Handler Utilities\n *\n * Shared utility functions for handlers\n */\n\nimport { ParserError } from \"@datrix/core\";\nimport { DatrixError, DatrixValidationError } from \"@datrix/core\";\nimport { DatrixApiError } from \"../errors/api-error\";\n\n/**\n * Create JSON response\n */\nexport function jsonResponse(data: unknown, status = 200): Response {\n\treturn new Response(JSON.stringify(data), {\n\t\tstatus,\n\t\theaders: { \"Content-Type\": \"application/json\" },\n\t});\n}\n\n/**\n * Generic DatrixError to Response converter\n * Handles ApiError (with status), DatrixValidationError (400), and base DatrixError\n */\nexport function datrixErrorResponse(\n\terror: DatrixApiError | DatrixError,\n): Response {\n\tlet status = 400;\n\n\tif (error instanceof DatrixApiError) {\n\t\tstatus = error.status;\n\t} else if (error instanceof DatrixValidationError) {\n\t\tstatus = 400;\n\t}\n\n\tconst serialized = error.toJSON();\n\n\treturn jsonResponse(\n\t\t{\n\t\t\terror: {\n\t\t\t\t...serialized,\n\t\t\t\ttype: error.name,\n\t\t\t},\n\t\t},\n\t\tstatus,\n\t);\n}\n\n/**\n * Create error response (Legacy support - will be phased out)\n * Use ApiError/datrixErrorResponse for new code\n */\nexport function errorResponse(\n\tmessage: string,\n\tcode: string,\n\tstatus = 500,\n): Response {\n\treturn jsonResponse({ error: { message, code } }, status);\n}\n\n/**\n * Create detailed parser error response\n * @deprecated Use datrixErrorResponse instead\n */\nexport function parserErrorResponse(error: ParserError): Response {\n\treturn datrixErrorResponse(error);\n}\n\n/**\n * Extract session ID from request cookies\n */\nexport function extractSessionId(request: Request): string | null {\n\tconst cookieHeader = request.headers.get(\"cookie\");\n\tif (!cookieHeader) return null;\n\n\tconst match = cookieHeader.match(/sessionId=([^;]+)/);\n\treturn match ? match[1]! : null;\n}\n","/**\n * API Error System\n *\n * Provides a unified error structure for the API package,\n * extending DatrixError with HTTP status handling and helpful context.\n */\n\nimport { DatrixError } from \"@datrix/core\";\n\n/**\n * Base API Error Class\n *\n * All API-specific errors should inherit from this class\n * or be created via its static helpers.\n */\nexport class DatrixApiError extends DatrixError {\n\t/** HTTP status code associated with this error */\n\tstatus: number;\n\n\tconstructor(message: string, options: ApiErrorOptions) {\n\t\tsuper(message, {\n\t\t\tcode: options.code,\n\t\t\toperation: options.operation || \"api:handler\",\n\t\t\t...(options.context && { context: options.context }),\n\t\t\t...(options.suggestion && { suggestion: options.suggestion }),\n\t\t\t...(options.expected && { expected: options.expected }),\n\t\t\t...(options.received !== undefined && { received: options.received }),\n\t\t\t...(options.cause && { cause: options.cause }),\n\t\t});\n\n\t\tthis.status = options.status || 500;\n\t}\n\n\t/**\n\t * Override toJSON to include status\n\t */\n\toverride toJSON() {\n\t\treturn {\n\t\t\t...super.toJSON(),\n\t\t\tstatus: this.status,\n\t\t};\n\t}\n}\n\nexport interface ApiErrorOptions {\n\tcode: string;\n\tstatus: number;\n\toperation?: string;\n\tcontext?: Record<string, unknown>;\n\tsuggestion?: string;\n\texpected?: string;\n\treceived?: unknown;\n\tcause?: Error;\n}\n\n/**\n * Handler Error Helpers\n *\n * Centralized error creation for routine API handlers.\n */\nexport const handlerError = {\n\tschemaNotFound(tableName: string, availableModels?: string[]): DatrixApiError {\n\t\treturn new DatrixApiError(`Model not found for table: ${tableName}`, {\n\t\t\tcode: \"SCHEMA_NOT_FOUND\",\n\t\t\tstatus: 404,\n\t\t\tcontext: { tableName, availableModels },\n\t\t\tsuggestion:\n\t\t\t\t\"Check if the table name is correct and the schema is properly defined.\",\n\t\t});\n\t},\n\n\tmodelNotSpecified(): DatrixApiError {\n\t\treturn new DatrixApiError(\"Model not specified in the request URL\", {\n\t\t\tcode: \"MODEL_NOT_SPECIFIED\",\n\t\t\tstatus: 400,\n\t\t\tsuggestion: \"Ensure the URL includes the model name (e.g., /api/users).\",\n\t\t});\n\t},\n\n\trecordNotFound(modelName: string, id: number | string): DatrixApiError {\n\t\treturn new DatrixApiError(`${modelName} record not found with ID: ${id}`, {\n\t\t\tcode: \"NOT_FOUND\",\n\t\t\tstatus: 404,\n\t\t\tcontext: { modelName, id },\n\t\t\tsuggestion: \"Verify the ID is correct or if the record has been deleted.\",\n\t\t});\n\t},\n\n\tinvalidBody(reason?: string): DatrixApiError {\n\t\treturn new DatrixApiError(\n\t\t\treason ? `Invalid request body: ${reason}` : \"Invalid request body\",\n\t\t\t{\n\t\t\t\tcode: \"INVALID_BODY\",\n\t\t\t\tstatus: 400,\n\t\t\t\tcontext: { reason },\n\t\t\t\tsuggestion:\n\t\t\t\t\t\"Ensure the request body is a valid JSON object and contains all required fields.\",\n\t\t\t},\n\t\t);\n\t},\n\n\tmissingId(operation: string): DatrixApiError {\n\t\treturn new DatrixApiError(`ID is required for ${operation}`, {\n\t\t\tcode: \"MISSING_ID\",\n\t\t\tstatus: 400,\n\t\t\tsuggestion: `Provide a valid ID in the URL for the ${operation} operation.`,\n\t\t});\n\t},\n\n\tmethodNotAllowed(method: string): DatrixApiError {\n\t\treturn new DatrixApiError(\n\t\t\t`HTTP Method ${method} is not allowed for this route`,\n\t\t\t{\n\t\t\t\tcode: \"METHOD_NOT_ALLOWED\",\n\t\t\t\tstatus: 405,\n\t\t\t\tcontext: { method },\n\t\t\t\tsuggestion:\n\t\t\t\t\t\"Check the API documentation for supported methods on this endpoint.\",\n\t\t\t},\n\t\t);\n\t},\n\n\tpermissionDenied(\n\t\treason: string,\n\t\tcontext?: Record<string, unknown>,\n\t): DatrixApiError {\n\t\treturn new DatrixApiError(\"Permission denied\", {\n\t\t\tcode: \"FORBIDDEN\",\n\t\t\tstatus: 403,\n\t\t\tcontext: { reason, ...context },\n\t\t\tsuggestion: \"Check your permissions or contact an administrator.\",\n\t\t});\n\t},\n\n\tunauthorized(reason?: string): DatrixApiError {\n\t\treturn new DatrixApiError(\"Unauthorized access\", {\n\t\t\tcode: \"UNAUTHORIZED\",\n\t\t\tstatus: 401,\n\t\t\tcontext: { reason },\n\t\t\tsuggestion: \"Provide valid authentication credentials.\",\n\t\t});\n\t},\n\n\tinternalError(message: string, cause?: Error): DatrixApiError {\n\t\treturn new DatrixApiError(message, {\n\t\t\tcode: \"INTERNAL_ERROR\",\n\t\t\tstatus: 500,\n\t\t\t...(cause && { cause }),\n\t\t});\n\t},\n\n\tconflict(reason: string, context?: Record<string, unknown>): DatrixApiError {\n\t\treturn new DatrixApiError(reason, {\n\t\t\tcode: \"CONFLICT\",\n\t\t\tstatus: 409,\n\t\t\t...(context && { context }),\n\t\t\tsuggestion:\n\t\t\t\t\"Ensure the resource you are trying to create does not already exist.\",\n\t\t});\n\t},\n};\n","/**\n * Auth Specific Errors\n */\n\nimport { DatrixApiError } from \"./api-error\";\n\n/**\n * Auth Error Helper\n */\nexport const authError = {\n\tinvalidCredentials(): DatrixApiError {\n\t\treturn new DatrixApiError(\"Invalid email or password\", {\n\t\t\tcode: \"INVALID_CREDENTIALS\",\n\t\t\tstatus: 401,\n\t\t\tsuggestion: \"Please check your email and password and try again.\",\n\t\t});\n\t},\n\n\tinvalidToken(reason?: string): DatrixApiError {\n\t\treturn new DatrixApiError(\"Invalid or expired authentication token\", {\n\t\t\tcode: \"INVALID_TOKEN\",\n\t\t\tstatus: 401,\n\t\t\tcontext: { reason },\n\t\t\tsuggestion: \"Please log in again to obtain a new session.\",\n\t\t});\n\t},\n\n\tmissingToken(): DatrixApiError {\n\t\treturn new DatrixApiError(\"Authentication token is missing\", {\n\t\t\tcode: \"MISSING_TOKEN\",\n\t\t\tstatus: 401,\n\t\t\tsuggestion:\n\t\t\t\t\"Include an Authorization header or a session cookie in your request.\",\n\t\t});\n\t},\n\n\tsessionExpired(): DatrixApiError {\n\t\treturn new DatrixApiError(\"Your session has expired\", {\n\t\t\tcode: \"SESSION_EXPIRED\",\n\t\t\tstatus: 401,\n\t\t\tsuggestion: \"Log in again to continue using the application.\",\n\t\t});\n\t},\n\n\taccountLocked(reason?: string): DatrixApiError {\n\t\treturn new DatrixApiError(\"This account has been locked\", {\n\t\t\tcode: \"ACCOUNT_LOCKED\",\n\t\t\tstatus: 403,\n\t\t\tcontext: { reason },\n\t\t\tsuggestion: \"Contact support to unlock your account.\",\n\t\t});\n\t},\n};\n","/**\n * Permission Middleware\n *\n * Schema-based permission checking with support for:\n * - Boolean permissions (true = everyone, false = no one)\n * - Role arrays (['admin', 'editor'])\n * - Permission functions ((ctx) => boolean)\n * - Mixed arrays with OR logic (['admin', (ctx) => ctx.user?.id === ctx.record?.authorId])\n */\n\nimport type { DatrixEntry, SchemaDefinition } from \"@datrix/core\";\nimport type {\n\tPermissionAction,\n\tPermissionValue,\n\tPermissionContext,\n\tPermissionFn,\n\tSchemaPermission,\n\tDefaultPermission,\n\tPermissionCheckResult,\n\tFieldPermission,\n\tFieldPermissionCheckResult,\n} from \"@datrix/core\";\nimport { isPermissionFn } from \"@datrix/core\";\nimport type { RequestContext } from \"./types\";\n\n/**\n * Build PermissionContext from RequestContext\n * Internal helper to satisfy PermissionFn signature\n */\nfunction buildPermCtx<T extends DatrixEntry>(\n\tctx: RequestContext,\n): PermissionContext<T> {\n\tconst permCtx: PermissionContext<T> = {\n\t\tuser: ctx.user ?? undefined,\n\t\tid: ctx.id,\n\t\taction: ctx.action,\n\t\tdatrix: ctx.datrix,\n\t};\n\n\t// Only add input if body exists (for exactOptionalPropertyTypes)\n\tif (ctx.body) {\n\t\t(permCtx as { input?: Record<string, unknown> }).input = ctx.body;\n\t}\n\n\treturn permCtx;\n}\n\n/**\n * Evaluate a single permission value\n *\n * @param value - Permission value to evaluate\n * @param ctx - Request context\n * @returns true if allowed, false otherwise\n */\nexport async function evaluatePermissionValue<TRoles extends string>(\n\tvalue: PermissionValue<TRoles> | undefined,\n\tctx: RequestContext,\n): Promise<boolean> {\n\t// Undefined means no restriction (allow)\n\tif (value === undefined) {\n\t\treturn true;\n\t}\n\n\t// Boolean: direct allow/deny\n\tif (typeof value === \"boolean\") {\n\t\treturn value;\n\t}\n\n\t// Function: evaluate with context\n\tif (isPermissionFn(value)) {\n\t\t// Create minimal context for permission function\n\t\tconst permCtx = buildPermCtx(ctx);\n\t\treturn await (value as PermissionFn)(permCtx);\n\t}\n\n\t// Array: check roles and/or functions (OR logic)\n\tif (Array.isArray(value)) {\n\t\t// No user means no role to check\n\t\tif (!ctx.user) {\n\t\t\t// But we still need to check if there are functions that might allow\n\t\t\tfor (const item of value) {\n\t\t\t\tif (isPermissionFn(item)) {\n\t\t\t\t\tconst permCtx = buildPermCtx(ctx);\n\t\t\t\t\tconst result = await (item as PermissionFn)(permCtx);\n\t\t\t\t\tif (result) return true;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\n\t\t// Check each item with OR logic\n\t\tfor (const item of value) {\n\t\t\tif (typeof item === \"string\") {\n\t\t\t\t// Role check\n\t\t\t\tif (ctx.user.role === item) {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t} else if (isPermissionFn(item)) {\n\t\t\t\t// Function check\n\t\t\t\tconst permCtx = buildPermCtx(ctx);\n\t\t\t\tconst result = await (item as PermissionFn)(permCtx);\n\t\t\t\tif (result) return true;\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\t// Unknown type, deny by default\n\treturn false;\n}\n\n/**\n * Check schema-level permission\n *\n * @param schema - Schema definition\n * @param ctx - Request context (contains action)\n * @param defaultPermission - Default permission if schema has no explicit permission\n * @returns Permission check result\n */\nexport async function checkSchemaPermission<TRoles extends string>(\n\tschema: SchemaDefinition<TRoles>,\n\tctx: RequestContext,\n\tdefaultPermission?: DefaultPermission<TRoles>,\n): Promise<PermissionCheckResult> {\n\tconst { action } = ctx;\n\n\t// Get permission value from schema or default\n\tconst schemaPermission = schema.permission as\n\t\t| SchemaPermission<TRoles>\n\t\t| undefined;\n\tlet permissionValue: PermissionValue<TRoles> | undefined;\n\n\tif (schemaPermission && schemaPermission[action] !== undefined) {\n\t\tpermissionValue = schemaPermission[action];\n\t} else if (defaultPermission && defaultPermission[action] !== undefined) {\n\t\tpermissionValue = defaultPermission[action];\n\t}\n\n\tconst allowed = await evaluatePermissionValue(permissionValue, ctx);\n\n\treturn {\n\t\tallowed,\n\t\treason: allowed\n\t\t\t? undefined\n\t\t\t: `Permission denied for ${action} on ${schema.name}`,\n\t};\n}\n\n/**\n * Check field-level read permissions and filter response\n *\n * @param schema - Schema definition\n * @param record - Record to filter\n * @param ctx - Request context\n * @returns Filtered record with denied fields removed\n */\nexport async function filterFieldsForRead<\n\tTRoles extends string,\n\tTRecord extends DatrixEntry,\n>(\n\tschema: SchemaDefinition<TRoles>,\n\trecord: TRecord,\n\tctx: RequestContext,\n): Promise<{ data: Partial<TRecord>; deniedFields: string[] }> {\n\tconst deniedFields: string[] = [];\n\tconst filtered: Partial<TRecord> = {};\n\n\tfor (const [fieldName, fieldValue] of Object.entries(record)) {\n\t\tconst fieldDef = schema.fields[fieldName];\n\n\t\t// If field not in schema, include it (system fields like id, createdAt)\n\t\tif (!fieldDef) {\n\t\t\t(filtered as Record<string, unknown>)[fieldName] = fieldValue;\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst fieldPermission = fieldDef.permission as\n\t\t\t| FieldPermission<TRoles>\n\t\t\t| undefined;\n\n\t\t// No permission defined = allow\n\t\tif (!fieldPermission || fieldPermission.read === undefined) {\n\t\t\t(filtered as Record<string, unknown>)[fieldName] = fieldValue;\n\t\t\tcontinue;\n\t\t}\n\n\t\t// Evaluate permission\n\t\tconst allowed = await evaluatePermissionValue(fieldPermission.read, ctx);\n\t\tif (allowed) {\n\t\t\t(filtered as Record<string, unknown>)[fieldName] = fieldValue;\n\t\t} else {\n\t\t\tdeniedFields.push(fieldName);\n\t\t}\n\t}\n\n\treturn { data: filtered, deniedFields };\n}\n\n/**\n * Check field-level write permissions\n *\n * @param schema - Schema definition\n * @param ctx - Request context (contains body as input)\n * @returns Result with denied fields (if any, should return 403)\n */\nexport async function checkFieldsForWrite<TRoles extends string>(\n\tschema: SchemaDefinition<TRoles>,\n\tctx: RequestContext,\n): Promise<FieldPermissionCheckResult> {\n\tconst deniedFields: string[] = [];\n\tconst input = ctx.body ?? {};\n\n\tfor (const fieldName of Object.keys(input)) {\n\t\tconst fieldDef = schema.fields[fieldName];\n\n\t\t// If field not in schema, skip (validator will handle)\n\t\tif (!fieldDef) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst fieldPermission = fieldDef.permission as\n\t\t\t| FieldPermission<TRoles>\n\t\t\t| undefined;\n\n\t\t// No permission defined = allow\n\t\tif (!fieldPermission || fieldPermission.write === undefined) {\n\t\t\tcontinue;\n\t\t}\n\n\t\t// Evaluate permission\n\t\tconst allowed = await evaluatePermissionValue(fieldPermission.write, ctx);\n\t\tif (!allowed) {\n\t\t\tdeniedFields.push(fieldName);\n\t\t}\n\t}\n\n\treturn {\n\t\tallowed: deniedFields.length === 0,\n\t\tdeniedFields: deniedFields.length > 0 ? deniedFields : undefined,\n\t};\n}\n\n/**\n * Map HTTP method to permission action\n */\nexport function methodToAction(method: string): PermissionAction {\n\tswitch (method.toUpperCase()) {\n\t\tcase \"GET\":\n\t\t\treturn \"read\";\n\t\tcase \"POST\":\n\t\t\treturn \"create\";\n\t\tcase \"PATCH\":\n\t\tcase \"PUT\":\n\t\t\treturn \"update\";\n\t\tcase \"DELETE\":\n\t\t\treturn \"delete\";\n\t\tdefault:\n\t\t\treturn \"read\";\n\t}\n}\n\n/**\n * Filter array of records for read permission (used for list endpoints)\n */\nexport async function filterRecordsForRead<\n\tTRoles extends string,\n\tTRecord extends DatrixEntry,\n>(\n\tschema: SchemaDefinition<TRoles>,\n\trecords: readonly TRecord[],\n\tctx: RequestContext,\n): Promise<Partial<TRecord>[]> {\n\tconst filtered: Partial<TRecord>[] = [];\n\n\tfor (const record of records) {\n\t\tconst { data } = await filterFieldsForRead(schema, record, ctx);\n\t\tfiltered.push(data);\n\t}\n\n\treturn filtered;\n}\n","/**\n * Main Query Parser\n *\n * Parses complete query strings into ParsedQuery.\n * Combines fields, where, populate, pagination, and sorting.\n */\n\nimport type { OrderByItem, OrderDirection } from \"@datrix/core\";\nimport {\n\tParserError,\n\tbuildErrorLocation,\n\ttype RawQueryParams,\n\ttype ParserOptions,\n\ttype ParsedPagination,\n\ttype ParsedSort,\n\ttype ParsedQuery,\n} from \"@datrix/core\";\nimport { validateFieldName } from \"@datrix/core\";\nimport { parseFields } from \"./fields-parser\";\nimport { parseWhere } from \"./where-parser\";\nimport { parsePopulate } from \"./populate-parser\";\nimport { paginationError, sortError } from \"./errors\";\nimport { DatrixEntry, DatrixRecord } from \"@datrix/core\";\n\n/**\n * Default parser options\n */\nconst DEFAULT_OPTIONS: Required<ParserOptions> = {\n\tmaxPageSize: 100,\n\tdefaultPageSize: 25,\n\tmaxPopulateDepth: 5,\n\tallowedOperators: [],\n\tstrictMode: false,\n};\n\n/**\n * Parse query parameters into ParsedQuery\n *\n * @param params - Raw query parameters\n * @param options - Parser options\n * @returns Result with ParsedQuery or ParserError\n */\nexport function parseQuery(\n\tparams: RawQueryParams,\n\toptions?: Partial<ParserOptions>,\n): ParsedQuery<DatrixEntry> {\n\tconst opts: Required<ParserOptions> = {\n\t\t...DEFAULT_OPTIONS,\n\t\t...options,\n\t};\n\n\tconst fields = parseFields(params);\n\tconst where = parseWhere(params);\n\tconst populate = parsePopulate(params, opts.maxPopulateDepth);\n\tconst pagination = parsePagination(params, opts);\n\tconst sort = parseSort(params);\n\n\tconst unknownParams = detectUnknownParams(params);\n\tif (unknownParams.length > 0) {\n\t\tthrow new ParserError(\n\t\t\t`Unknown query parameters: ${unknownParams.join(\", \")}`,\n\t\t\t{\n\t\t\t\tcode: \"UNKNOWN_PARAMETER\",\n\t\t\t\tparser: \"query\",\n\t\t\t\tlocation: buildErrorLocation(unknownParams),\n\t\t\t\treceived: unknownParams,\n\t\t\t\texpected:\n\t\t\t\t\t\"Known parameters: fields, where, populate, page, pageSize, sort\",\n\t\t\t\tsuggestion:\n\t\t\t\t\t\"Check for typos. Common mistake: use 'where' instead of 'filters'.\",\n\t\t\t},\n\t\t);\n\t}\n\n\tconst result = {\n\t\t...(fields !== undefined && fields !== \"*\" && { select: fields }),\n\t\t...(where !== undefined && { where }),\n\t\t...(populate !== undefined && { populate }),\n\t\t...(pagination !== undefined && {\n\t\t\tpage: pagination.page ?? 1,\n\t\t\tpageSize: pagination.pageSize ?? opts.defaultPageSize,\n\t\t}),\n\t\t...(sort !== undefined &&\n\t\t\tArray.isArray(sort) &&\n\t\t\tsort.length > 0 && { orderBy: sort }),\n\t} as ParsedQuery<DatrixEntry>;\n\n\treturn result;\n}\n\n/**\n * Parse pagination parameters\n * Throws ParserError on validation failure\n */\nfunction parsePagination(\n\tparams: RawQueryParams,\n\toptions: Required<ParserOptions>,\n): ParsedPagination | undefined {\n\tconst { page, pageSize } = params;\n\n\t// Parse page/pageSize with defaults\n\tconst parsedPage = page !== undefined ? parseInt(String(page), 10) : 1;\n\tconst parsedPageSize =\n\t\tpageSize !== undefined\n\t\t\t? parseInt(String(pageSize), 10)\n\t\t\t: options.defaultPageSize;\n\n\t// Maximum safe page number to prevent overflow\n\tconst MAX_PAGE_NUMBER = 1000000;\n\n\t// Validate page\n\tif (isNaN(parsedPage) || parsedPage < 1) {\n\t\tpaginationError.invalidPage(page ?? \"\", [\"page\"]);\n\t}\n\n\tif (parsedPage > MAX_PAGE_NUMBER) {\n\t\tpaginationError.maxPageNumberExceeded(parsedPage, MAX_PAGE_NUMBER, [\n\t\t\t\"page\",\n\t\t]);\n\t}\n\n\t// Validate pageSize\n\tif (isNaN(parsedPageSize) || parsedPageSize < 1) {\n\t\tpaginationError.invalidPageSize(pageSize ?? \"\", [\"pageSize\"]);\n\t}\n\n\tif (parsedPageSize > options.maxPageSize) {\n\t\tpaginationError.maxPageSizeExceeded(parsedPageSize, options.maxPageSize, [\n\t\t\t\"pageSize\",\n\t\t]);\n\t}\n\n\treturn {\n\t\tpage: parsedPage,\n\t\tpageSize: parsedPageSize,\n\t};\n}\n\n/**\n * Parse sort parameters\n * Throws ParserError on validation failure\n *\n * Examples:\n * ?sort=name -> orderBy: [{ field: 'name', direction: 'asc' }]\n * ?sort=-createdAt -> orderBy: [{ field: 'createdAt', direction: 'desc' }]\n * ?sort=name,-createdAt -> multiple sorts\n */\nfunction parseSort(params: RawQueryParams): ParsedSort | undefined {\n\tconst sortParam = params[\"sort\"];\n\n\tif (sortParam === undefined) {\n\t\treturn undefined;\n\t}\n\n\t// Handle empty or whitespace-only sort\n\tif (typeof sortParam === \"string\" && sortParam.trim() === \"\") {\n\t\tsortError.emptyValue([]);\n\t}\n\n\tconst sorts: OrderByItem<DatrixRecord>[] = [];\n\n\t// Handle comma-separated sorts\n\tconst sortStrings =\n\t\ttypeof sortParam === \"string\"\n\t\t\t? sortParam.split(\",\").map((s) => s.trim())\n\t\t\t: Array.isArray(sortParam)\n\t\t\t\t? sortParam.map((s) => String(s).trim())\n\t\t\t\t: [String(sortParam).trim()];\n\n\tfor (const sortStr of sortStrings) {\n\t\tif (!sortStr) {\n\t\t\tcontinue;\n\t\t}\n\n\t\t// Check for descending order (leading -)\n\t\tconst isDescending = sortStr.startsWith(\"-\");\n\t\tconst field = isDescending ? sortStr.slice(1) : sortStr;\n\n\t\tif (!field) {\n\t\t\tsortError.invalidFieldName(sortStr, [sortStr]);\n\t\t}\n\n\t\tconst validation = validateFieldName(field);\n\t\tif (!validation.valid) {\n\t\t\tsortError.invalidFieldName(sortStr, [sortStr], {\n\t\t\t\tfieldValidationReason: validation.reason,\n\t\t\t});\n\t\t}\n\n\t\tconst direction: OrderDirection = isDescending ? \"desc\" : \"asc\";\n\t\tsorts.push({ field, direction });\n\t}\n\n\treturn sorts.length > 0 ? sorts : undefined;\n}\n\n/**\n * Known query parameter prefixes\n *\n * Any key not matching these is considered unknown.\n * This catches typos like \"filters\" (should be \"where\"),\n * \"limit\" (should be \"pageSize\"), etc.\n */\nconst KNOWN_PARAM_PREFIXES = [\n\t\"fields\",\n\t\"where\",\n\t\"populate\",\n\t\"page\",\n\t\"pageSize\",\n\t\"sort\",\n] as const;\n\n/**\n * Detect unknown/unrecognized query parameters\n *\n * Returns list of parameter keys that don't match any known prefix.\n * This prevents silent failures where typos like \"filters\" are ignored.\n */\nfunction detectUnknownParams(params: RawQueryParams): string[] {\n\tconst unknownKeys: string[] = [];\n\n\tfor (const key of Object.keys(params)) {\n\t\tconst isKnown = KNOWN_PARAM_PREFIXES.some(\n\t\t\t(prefix) => key === prefix || key.startsWith(`${prefix}[`),\n\t\t);\n\n\t\tif (!isKnown) {\n\t\t\tunknownKeys.push(key);\n\t\t}\n\t}\n\n\treturn unknownKeys;\n}\n","/**\n * Fields Parser\n *\n * Parses fields query params into SelectClause.\n * Examples:\n * ?fields[0]=name&fields[1]=email\n * ?fields=name,email\n */\n\nimport type { RawQueryParams } from \"@datrix/core\";\nimport { MAX_ARRAY_INDEX, validateFieldName } from \"@datrix/core\";\nimport { fieldsError } from \"./errors\";\n\n/**\n * Parse fields parameter\n * Throws ParserError on validation failure\n *\n * @param params - Raw query parameters\n * @returns SelectClause (string[] | '*' | undefined)\n * @throws {ParserError} When validation fails\n */\nexport function parseFields(\n\tparams: RawQueryParams,\n): string[] | \"*\" | undefined {\n\t// Check for suspicious parameters (fields[extra], fields_injection, etc.)\n\tconst suspiciousParams = Object.keys(params).filter(\n\t\t(key) =>\n\t\t\tkey.startsWith(\"fields\") &&\n\t\t\tkey !== \"fields\" &&\n\t\t\t!key.match(/^fields\\[\\d+\\]$/), // Allow fields[0], fields[1], etc.\n\t);\n\n\tif (suspiciousParams.length > 0) {\n\t\tfieldsError.suspiciousParams(suspiciousParams, []);\n\t}\n\n\t// Handle array format: fields[0]=name&fields[2]=email (sparse arrays allowed)\n\tconst arrayFields = extractArrayFields(params);\n\tif (arrayFields.length > 0) {\n\t\treturn validateAndReturn(arrayFields);\n\t}\n\n\t// Check for fields parameter\n\tconst fieldsParam = params[\"fields\"];\n\n\tif (fieldsParam === undefined) {\n\t\t// No fields specified, return wildcard (will select all)\n\t\treturn \"*\";\n\t}\n\n\t// Handle wildcard\n\tif (fieldsParam === \"*\") {\n\t\treturn \"*\";\n\t}\n\n\t// Handle comma-separated format: fields=name,email\n\tif (typeof fieldsParam === \"string\") {\n\t\tconst fields = fieldsParam\n\t\t\t.split(\",\")\n\t\t\t.map((f) => f.trim())\n\t\t\t.filter(Boolean);\n\n\t\t// Reject if all fields are empty after trimming\n\t\tif (fields.length === 0) {\n\t\t\tfieldsError.emptyValue([]);\n\t\t}\n\n\t\treturn validateAndReturn(fields);\n\t}\n\n\t// Handle array (from frameworks that parse query strings into arrays)\n\tif (Array.isArray(fieldsParam)) {\n\t\tconst fields = fieldsParam.map((f) => String(f).trim()).filter(Boolean);\n\n\t\t// Reject if all fields are empty after trimming\n\t\tif (fields.length === 0) {\n\t\t\tfieldsError.emptyValue([]);\n\t\t}\n\n\t\treturn validateAndReturn(fields);\n\t}\n\n\t// Invalid format\n\tfieldsError.invalidFormat([]);\n\n\treturn undefined;\n}\n\n/**\n * Extract fields from array-style parameters\n * Handles sparse arrays: fields[0]=name&fields[2]=email (fields[1] can be missing)\n *\n * This allows UI checkboxes where users select specific fields,\n * resulting in non-sequential indices.\n */\nfunction extractArrayFields(params: RawQueryParams): string[] {\n\tconst fields: string[] = [];\n\n\t// Find all fields[N] parameters\n\tfor (const key in params) {\n\t\tconst match = key.match(/^fields\\[(\\d+)\\]$/);\n\t\tif (!match) continue;\n\n\t\tconst index = parseInt(match[1]!, 10);\n\n\t\t// Prevent DoS attacks with extremely large indices\n\t\tif (index >= MAX_ARRAY_INDEX) {\n\t\t\tcontinue; // Skip invalid indices\n\t\t}\n\n\t\tconst value = params[key];\n\t\tif (typeof value === \"string\") {\n\t\t\tfields.push(value.trim());\n\t\t} else if (Array.isArray(value)) {\n\t\t\t// Framework might parse duplicate params as array\n\t\t\tfields.push(...value.map((v) => String(v).trim()));\n\t\t}\n\t}\n\n\treturn fields;\n}\n\n/**\n * Validate field names and return result\n */\nfunction validateAndReturn(fields: readonly string[]): string[] | \"*\" {\n\tif (fields.length === 0) {\n\t\treturn \"*\";\n\t}\n\n\t// Validate field names (alphanumeric, underscores, dots for nested fields)\n\tconst invalidFieldsWithReasons: Array<{ field: string; reason: string }> = [];\n\n\tfor (const field of fields) {\n\t\tconst validation = validateFieldName(field);\n\t\tif (!validation.valid) {\n\t\t\tinvalidFieldsWithReasons.push({ field, reason: validation.reason });\n\t\t}\n\t}\n\n\tif (invalidFieldsWithReasons.length > 0) {\n\t\tconst invalidFields = invalidFieldsWithReasons.map((item) => item.field);\n\t\tconst reasons = invalidFieldsWithReasons.map((item) => item.reason);\n\n\t\tfieldsError.invalidFieldNames(invalidFields, [], {\n\t\t\tvalidationReasons: reasons,\n\t\t});\n\t}\n\n\treturn fields as string[];\n}\n","/**\n * Parser Error Helpers\n *\n * Centralized error creation for all parsers.\n * Provides clean, type-safe error handling with rich context.\n */\n\nimport {\n\tParserError,\n\tbuildErrorLocation,\n\ttype WhereErrorContext,\n\ttype PopulateErrorContext,\n\ttype FieldsErrorContext,\n\ttype PaginationErrorContext,\n\ttype SortErrorContext,\n} from \"@datrix/core\";\nimport { MAX_WHERE_VALUE_LENGTH, MAX_LOGICAL_NESTING_DEPTH } from \"@datrix/core\";\n\n/**\n * Where Parser Errors\n */\nexport const whereError = {\n\tinvalidOperator(\n\t\toperator: string,\n\t\tpath: string[],\n\t\tcontext?: Partial<WhereErrorContext>,\n\t) {\n\t\tthrow new ParserError(`Invalid WHERE operator: ${operator}`, {\n\t\t\tcode: \"INVALID_OPERATOR\",\n\t\t\tparser: \"where\",\n\t\t\tlocation: buildErrorLocation([\"where\", ...path], {\n\t\t\t\tqueryParam: context?.operatorPath,\n\t\t\t}),\n\t\t\treceived: operator,\n\t\t\texpected:\n\t\t\t\t\"One of: $eq, $ne, $gt, $gte, $lt, $lte, $in, $nin, $contains, $startsWith, $endsWith, $like, $ilike, $null, $notNull, $and, $or, $not\",\n\t\t\tsuggestion:\n\t\t\t\t\"Use a valid WHERE operator. See documentation for full list.\",\n\t\t\tcontext: {\n\t\t\t\toperator,\n\t\t\t\t...context,\n\t\t\t},\n\t\t});\n\t},\n\n\tinvalidFieldName(\n\t\tfieldName: string,\n\t\tpath: string[],\n\t\tcontext?: Partial<WhereErrorContext>,\n\t) {\n\t\tconst reasonDetail = context?.fieldValidationReason\n\t\t\t? ` (Reason: ${context.fieldValidationReason})`\n\t\t\t: \"\";\n\n\t\tthrow new ParserError(\n\t\t\t`Invalid field name in WHERE clause: ${fieldName}${reasonDetail}`,\n\t\t\t{\n\t\t\t\tcode: \"INVALID_FIELD_NAME\",\n\t\t\t\tparser: \"where\",\n\t\t\t\tlocation: buildErrorLocation([\"where\", ...path]),\n\t\t\t\treceived: fieldName,\n\t\t\t\texpected:\n\t\t\t\t\t\"Field name must start with letter/underscore and contain only alphanumeric characters, underscores, and dots\",\n\t\t\t\tsuggestion:\n\t\t\t\t\t\"Use valid field names (e.g., 'name', 'user_id', 'profile.age')\",\n\t\t\t\tcontext: {\n\t\t\t\t\toperator: fieldName,\n\t\t\t\t\t...context,\n\t\t\t\t},\n\t\t\t},\n\t\t);\n\t},\n\n\tinvalidArrayIndex(\n\t\tindex: string,\n\t\toperator: string,\n\t\tpath: string[],\n\t\tcontext?: Partial<WhereErrorContext>,\n\t) {\n\t\tthrow new ParserError(\n\t\t\t`Array index [${index}] can only follow array operators ($or, $and, $not, $in, $nin), found after: ${context?.previousOperator || \"unknown\"}`,\n\t\t\t{\n\t\t\t\tcode: \"ARRAY_INDEX_ERROR\",\n\t\t\t\tparser: \"where\",\n\t\t\t\tlocation: buildErrorLocation([\"where\", ...path], {\n\t\t\t\t\tindex: parseInt(index, 10),\n\t\t\t\t\tqueryParam: context?.operatorPath,\n\t\t\t\t}),\n\t\t\t\treceived: index,\n\t\t\t\texpected: \"Array index after $or, $and, $not, $in, or $nin\",\n\t\t\t\tsuggestion: \"Array indices can only be used with array operators\",\n\t\t\t\tcontext: {\n\t\t\t\t\toperator,\n\t\t\t\t\tarrayIndex: parseInt(index, 10),\n\t\t\t\t\t...context,\n\t\t\t\t},\n\t\t\t},\n\t\t);\n\t},\n\n\tarrayIndexAtStart(index: string, _path: string[]): never {\n\t\tthrow new ParserError(\n\t\t\t\"Array index cannot appear at the beginning of WHERE clause\",\n\t\t\t{\n\t\t\t\tcode: \"ARRAY_INDEX_ERROR\",\n\t\t\t\tparser: \"where\",\n\t\t\t\tlocation: buildErrorLocation([\"where\"], {\n\t\t\t\t\tindex: parseInt(index, 10),\n\t\t\t\t}),\n\t\t\t\treceived: index,\n\t\t\t\texpected: \"Field name or operator before array index\",\n\t\t\t\tsuggestion:\n\t\t\t\t\t\"WHERE clause must start with a field name, not an array index\",\n\t\t\t\tcontext: {\n\t\t\t\t\tarrayIndex: parseInt(index, 10),\n\t\t\t\t},\n\t\t\t},\n\t\t);\n\t},\n\n\tinvalidArrayIndexFormat(index: string, operator: string, path: string[]) {\n\t\tthrow new ParserError(\n\t\t\t`Invalid array index in ${operator}: ${index} (must be non-negative integer)`,\n\t\t\t{\n\t\t\t\tcode: \"ARRAY_INDEX_ERROR\",\n\t\t\t\tparser: \"where\",\n\t\t\t\tlocation: buildErrorLocation([\"where\", ...path]),\n\t\t\t\treceived: index,\n\t\t\t\texpected: \"Non-negative integer (0, 1, 2, ...)\",\n\t\t\t\tsuggestion: \"Use valid array indices starting from 0\",\n\t\t\t\tcontext: {\n\t\t\t\t\toperator,\n\t\t\t\t\tarrayIndex: NaN,\n\t\t\t\t},\n\t\t\t},\n\t\t);\n\t},\n\n\tarrayIndexNotStartingFromZero(\n\t\tfirstIndex: number,\n\t\toperator: string,\n\t\tpath: string[],\n\t) {\n\t\tthrow new ParserError(\n\t\t\t`Array indices for ${operator} must start from 0, found: ${firstIndex}`,\n\t\t\t{\n\t\t\t\tcode: \"CONSECUTIVE_INDEX_ERROR\",\n\t\t\t\tparser: \"where\",\n\t\t\t\tlocation: buildErrorLocation([\"where\", ...path], {\n\t\t\t\t\tindex: firstIndex,\n\t\t\t\t}),\n\t\t\t\treceived: firstIndex,\n\t\t\t\texpected: \"Array indices starting from 0\",\n\t\t\t\tsuggestion: \"Start array indices at 0: use [0], [1], [2], etc.\",\n\t\t\t\tcontext: {\n\t\t\t\t\toperator,\n\t\t\t\t\tarrayIndex: firstIndex,\n\t\t\t\t},\n\t\t\t},\n\t\t);\n\t},\n\n\tarrayIndexNotConsecutive(\n\t\tmissingIndex: number,\n\t\toperator: string,\n\t\tpath: string[],\n\t\tfoundIndices?: number[],\n\t) {\n\t\tconst indicesStr = foundIndices\n\t\t\t? `. Found: [${foundIndices.join(\", \")}]`\n\t\t\t: \"\";\n\n\t\tthrow new ParserError(\n\t\t\t`Array indices for ${operator} must be consecutive. Missing index: ${missingIndex}${indicesStr}`,\n\t\t\t{\n\t\t\t\tcode: \"CONSECUTIVE_INDEX_ERROR\",\n\t\t\t\tparser: \"where\",\n\t\t\t\tlocation: buildErrorLocation([\"where\", ...path], {\n\t\t\t\t\tindex: missingIndex,\n\t\t\t\t}),\n\t\t\t\treceived: foundIndices\n\t\t\t\t\t? `Indices: [${foundIndices.join(\", \")}]`\n\t\t\t\t\t: `Gap at index ${missingIndex}`,\n\t\t\t\texpected: \"Consecutive indices: [0, 1, 2, ...]\",\n\t\t\t\tsuggestion: `Add ${operator}[${missingIndex}] to fix the gap`,\n\t\t\t\tcontext: {\n\t\t\t\t\toperator,\n\t\t\t\t\tmissingIndex,\n\t\t\t\t\tfoundIndices,\n\t\t\t\t},\n\t\t\t},\n\t\t);\n\t},\n\n\tmaxValueLength(actualLength: number, path: string[]) {\n\t\tthrow new ParserError(\n\t\t\t`WHERE value exceeds maximum length of ${MAX_WHERE_VALUE_LENGTH} characters`,\n\t\t\t{\n\t\t\t\tcode: \"MAX_LENGTH_EXCEEDED\",\n\t\t\t\tparser: \"where\",\n\t\t\t\tlocation: buildErrorLocation([\"where\", ...path]),\n\t\t\t\treceived: `${actualLength} characters`,\n\t\t\t\texpected: `Maximum ${MAX_WHERE_VALUE_LENGTH} characters`,\n\t\t\t\tsuggestion:\n\t\t\t\t\t\"Reduce the length of your query value or use a different approach\",\n\t\t\t\tcontext: {\n\t\t\t\t\toperator: \"value_length\",\n\t\t\t\t},\n\t\t\t},\n\t\t);\n\t},\n\n\tmaxDepthExceeded(depth: number, path: string[]) {\n\t\tconst pathStr = path.length > 0 ? ` at path: ${path.join(\".\")}` : \"\";\n\n\t\tthrow new ParserError(\n\t\t\t`WHERE clause nesting depth exceeds maximum of ${MAX_LOGICAL_NESTING_DEPTH}${pathStr}`,\n\t\t\t{\n\t\t\t\tcode: \"MAX_DEPTH_EXCEEDED\",\n\t\t\t\tparser: \"where\",\n\t\t\t\tlocation: buildErrorLocation([\"where\", ...path], {\n\t\t\t\t\tdepth,\n\t\t\t\t}),\n\t\t\t\treceived: `Depth: ${depth}${pathStr}`,\n\t\t\t\texpected: `Maximum depth: ${MAX_LOGICAL_NESTING_DEPTH}`,\n\t\t\t\tsuggestion: \"Simplify query structure or split into multiple requests\",\n\t\t\t\tcontext: {\n\t\t\t\t\toperator: \"nesting_depth\",\n\t\t\t\t\tcurrentPath: path.join(\".\"),\n\t\t\t\t\tdepth,\n\t\t\t\t},\n\t\t\t},\n\t\t);\n\t},\n\n\temptyLogicalOperator(operator: string, path: string[]) {\n\t\tthrow new ParserError(\n\t\t\t`Logical operator ${operator} requires at least one condition`,\n\t\t\t{\n\t\t\t\tcode: \"EMPTY_VALUE\",\n\t\t\t\tparser: \"where\",\n\t\t\t\tlocation: buildErrorLocation([\"where\", ...path]),\n\t\t\t\treceived: \"empty array\",\n\t\t\t\texpected: \"At least one condition\",\n\t\t\t\tsuggestion: `Add at least one condition to ${operator} operator`,\n\t\t\t\tcontext: {\n\t\t\t\t\toperator,\n\t\t\t\t},\n\t\t\t},\n\t\t);\n\t},\n\n\temptyArrayOperator(operator: string, path: string[]) {\n\t\tthrow new ParserError(`Operator ${operator} requires a non-empty array`, {\n\t\t\tcode: \"EMPTY_VALUE\",\n\t\t\tparser: \"where\",\n\t\t\tlocation: buildErrorLocation([\"where\", ...path]),\n\t\t\treceived: \"empty array\",\n\t\t\texpected: \"Non-empty array\",\n\t\t\tsuggestion: `Provide at least one value for ${operator} operator`,\n\t\t\tcontext: {\n\t\t\t\toperator,\n\t\t\t},\n\t\t});\n\t},\n\n\tinvalidOperatorValue(\n\t\toperator: string,\n\t\tvalueType: string,\n\t\tpath: string[],\n\t\treceivedValue?: unknown,\n\t) {\n\t\tconst valuePreview =\n\t\t\treceivedValue !== undefined\n\t\t\t\t? `: ${JSON.stringify(receivedValue).slice(0, 50)}`\n\t\t\t\t: \"\";\n\n\t\tthrow new ParserError(\n\t\t\t`Operator ${operator} requires array but received ${valueType}${valuePreview}`,\n\t\t\t{\n\t\t\t\tcode: \"INVALID_VALUE_TYPE\",\n\t\t\t\tparser: \"where\",\n\t\t\t\tlocation: buildErrorLocation([\"where\", ...path]),\n\t\t\t\treceived: `${valueType}${valuePreview}`,\n\t\t\t\texpected: \"array (e.g., [1, 2, 3])\",\n\t\t\t\tsuggestion: `Use array format: where[field][${operator}][0]=value1&where[field][${operator}][1]=value2`,\n\t\t\t\tcontext: {\n\t\t\t\t\toperator,\n\t\t\t\t\treceivedType: valueType,\n\t\t\t\t},\n\t\t\t},\n\t\t);\n\t},\n};\n\n/**\n * Populate Parser Errors\n */\nexport const populateError = {\n\tinvalidRelation(\n\t\trelation: string,\n\t\tpath: string[],\n\t\tcontext?: Partial<PopulateErrorContext>,\n\t) {\n\t\tthrow new ParserError(`Invalid relation name: ${relation}`, {\n\t\t\tcode: \"INVALID_FIELD_NAME\",\n\t\t\tparser: \"populate\",\n\t\t\tlocation: buildErrorLocation([\"populate\", ...path], {\n\t\t\t\tdepth: context?.currentDepth,\n\t\t\t}),\n\t\t\treceived: relation,\n\t\t\texpected:\n\t\t\t\t\"Relation name must start with letter/underscore and contain only alphanumeric characters and underscores\",\n\t\t\tsuggestion: \"Use valid relation names (e.g., 'author', 'user_profile')\",\n\t\t\tcontext: {\n\t\t\t\trelation,\n\t\t\t\t...context,\n\t\t\t},\n\t\t});\n\t},\n\n\tmaxDepthExceeded(\n\t\tdepth: number,\n\t\tmaxDepth: number,\n\t\tpath: string[],\n\t\tcontext?: Partial<PopulateErrorContext>,\n\t) {\n\t\tthrow new ParserError(\"Maximum populate depth exceeded\", {\n\t\t\tcode: \"MAX_DEPTH_EXCEEDED\",\n\t\t\tparser: \"populate\",\n\t\t\tlocation: buildErrorLocation([\"populate\", ...path], {\n\t\t\t\tdepth,\n\t\t\t}),\n\t\t\treceived: depth,\n\t\t\texpected: `Maximum depth: ${maxDepth}`,\n\t\t\tsuggestion:\n\t\t\t\t\"Reduce nesting level or increase maxPopulateDepth in parser options\",\n\t\t\tcontext: {\n\t\t\t\tcurrentDepth: depth,\n\t\t\t\tmaxDepth,\n\t\t\t\trelationPath: path.join(\".\"),\n\t\t\t\t...context,\n\t\t\t},\n\t\t});\n\t},\n\n\temptyValue(path: string[]) {\n\t\tthrow new ParserError(\"Populate value cannot be empty\", {\n\t\t\tcode: \"EMPTY_VALUE\",\n\t\t\tparser: \"populate\",\n\t\t\tlocation: buildErrorLocation([\"populate\", ...path]),\n\t\t\treceived: \"empty string\",\n\t\t\texpected: \"Relation name or wildcard (*)\",\n\t\t\tsuggestion: \"Provide a relation name or use * to populate all relations\",\n\t\t\tcontext: {},\n\t\t});\n\t},\n\n\tinvalidType(type: string, path: string[]) {\n\t\tthrow new ParserError(\"Populate value must be a string or array\", {\n\t\t\tcode: \"INVALID_VALUE_TYPE\",\n\t\t\tparser: \"populate\",\n\t\t\tlocation: buildErrorLocation([\"populate\", ...path]),\n\t\t\treceived: type,\n\t\t\texpected: \"string or array\",\n\t\t\tsuggestion: \"Use a string (e.g., 'author') or array format\",\n\t\t\tcontext: {},\n\t\t});\n\t},\n\n\tinvalidFieldName(\n\t\tfieldName: string,\n\t\tpath: string[],\n\t\tcontext?: Partial<PopulateErrorContext>,\n\t): never {\n\t\tconst reasonDetail = context?.fieldValidationReason\n\t\t\t? ` (Reason: ${context.fieldValidationReason})`\n\t\t\t: \"\";\n\n\t\tthrow new ParserError(\n\t\t\t`Invalid field name in populate: ${fieldName}${reasonDetail}`,\n\t\t\t{\n\t\t\t\tcode: \"INVALID_FIELD_NAME\",\n\t\t\t\tparser: \"populate\",\n\t\t\t\tlocation: buildErrorLocation([\"populate\", ...path]),\n\t\t\t\treceived: fieldName,\n\t\t\t\texpected:\n\t\t\t\t\t\"Field name must start with letter/underscore and contain only alphanumeric characters, underscores, and dots\",\n\t\t\t\tsuggestion:\n\t\t\t\t\t\"Use valid field names (e.g., 'name', 'user_id', 'profile.age')\",\n\t\t\t\tcontext: {\n\t\t\t\t\tfieldName,\n\t\t\t\t\t...context,\n\t\t\t\t},\n\t\t\t},\n\t\t);\n\t},\n};\n\n/**\n * Fields Parser Errors\n */\nexport const fieldsError = {\n\tinvalidFieldNames(\n\t\tinvalidFields: readonly string[],\n\t\tpath: string[],\n\t\tcontext?: Partial<FieldsErrorContext>,\n\t) {\n\t\tconst reasonDetail =\n\t\t\tcontext?.validationReasons && context.validationReasons.length > 0\n\t\t\t\t? ` (Reasons: ${context.validationReasons.join(\", \")})`\n\t\t\t\t: \"\";\n\n\t\tthrow new ParserError(\n\t\t\t`Invalid field names: ${invalidFields.join(\", \")}${reasonDetail}`,\n\t\t\t{\n\t\t\t\tcode: \"INVALID_FIELD_NAME\",\n\t\t\t\tparser: \"fields\",\n\t\t\t\tlocation: buildErrorLocation([\"fields\", ...path]),\n\t\t\t\treceived: invalidFields,\n\t\t\t\texpected:\n\t\t\t\t\t\"Field names must start with letter/underscore and contain only alphanumeric characters, underscores, and dots\",\n\t\t\t\tsuggestion:\n\t\t\t\t\t\"Use valid field names (e.g., 'name', 'user_id', 'profile.age')\",\n\t\t\t\tcontext: {\n\t\t\t\t\tinvalidFields: invalidFields as string[],\n\t\t\t\t\t...context,\n\t\t\t\t},\n\t\t\t},\n\t\t);\n\t},\n\n\temptyValue(path: string[]) {\n\t\tthrow new ParserError(\n\t\t\t\"Fields parameter is empty or contains only whitespace\",\n\t\t\t{\n\t\t\t\tcode: \"EMPTY_VALUE\",\n\t\t\t\tparser: \"fields\",\n\t\t\t\tlocation: buildErrorLocation([\"fields\", ...path]),\n\t\t\t\treceived: \"empty string\",\n\t\t\t\texpected: \"Field name(s) or wildcard (*)\",\n\t\t\t\tsuggestion:\n\t\t\t\t\t\"Provide field names (e.g., 'name,email') or use * for all fields\",\n\t\t\t\tcontext: {},\n\t\t\t},\n\t\t);\n\t},\n\n\tsuspiciousParams(params: readonly string[], path: string[]) {\n\t\tthrow new ParserError(`Unknown fields parameters: ${params.join(\", \")}`, {\n\t\t\tcode: \"UNKNOWN_PARAMETER\",\n\t\t\tparser: \"fields\",\n\t\t\tlocation: buildErrorLocation([\"fields\", ...path]),\n\t\t\treceived: params,\n\t\t\texpected: \"fields or fields[N] format\",\n\t\t\tsuggestion:\n\t\t\t\t\"Use 'fields=name,email' or 'fields[0]=name&fields[1]=email' format\",\n\t\t\tcontext: {\n\t\t\t\tsuspiciousParams: params as string[],\n\t\t\t},\n\t\t});\n\t},\n\n\tinvalidFormat(path: string[]) {\n\t\tthrow new ParserError(\"Invalid fields format\", {\n\t\t\tcode: \"INVALID_SYNTAX\",\n\t\t\tparser: \"fields\",\n\t\t\tlocation: buildErrorLocation([\"fields\", ...path]),\n\t\t\treceived: \"unknown format\",\n\t\t\texpected: \"string or array\",\n\t\t\tsuggestion: \"Use 'fields=name,email' or 'fields[0]=name' format\",\n\t\t\tcontext: {},\n\t\t});\n\t},\n};\n\n/**\n * Pagination Parser Errors\n */\nexport const paginationError = {\n\tinvalidLimit(\n\t\tvalue: string | number | readonly string[] | undefined,\n\t\tpath: string[],\n\t\tcontext?: Partial<PaginationErrorContext>,\n\t) {\n\t\tthrow new ParserError(`Invalid limit value: \"${value}\"`, {\n\t\t\tcode: \"INVALID_PAGINATION\",\n\t\t\tparser: \"pagination\",\n\t\t\tlocation: buildErrorLocation([\"pagination\", ...path]),\n\t\t\treceived: value,\n\t\t\texpected: \"Positive integer\",\n\t\t\tsuggestion: \"Provide a positive integer for limit (e.g., limit=10)\",\n\t\t\tcontext: {\n\t\t\t\tparameter: \"limit\",\n\t\t\t\t...context,\n\t\t\t},\n\t\t});\n\t},\n\n\tinvalidOffset(\n\t\tvalue: string | number | readonly string[] | undefined,\n\t\tpath: string[],\n\t\tcontext?: Partial<PaginationErrorContext>,\n\t) {\n\t\tthrow new ParserError(`Invalid offset value: \"${value}\"`, {\n\t\t\tcode: \"INVALID_PAGINATION\",\n\t\t\tparser: \"pagination\",\n\t\t\tlocation: buildErrorLocation([\"pagination\", ...path]),\n\t\t\treceived: value,\n\t\t\texpected: \"Non-negative integer\",\n\t\t\tsuggestion: \"Provide a non-negative integer for offset (e.g., offset=0)\",\n\t\t\tcontext: {\n\t\t\t\tparameter: \"offset\",\n\t\t\t\t...context,\n\t\t\t},\n\t\t});\n\t},\n\n\tinvalidPage(\n\t\tvalue: string | number | readonly string[],\n\t\tpath: string[],\n\t\tcontext?: Partial<PaginationErrorContext>,\n\t) {\n\t\tthrow new ParserError(`Invalid page value: \"${value}\" (must be >= 1)`, {\n\t\t\tcode: \"INVALID_PAGINATION\",\n\t\t\tparser: \"pagination\",\n\t\t\tlocation: buildErrorLocation([\"pagination\", ...path]),\n\t\t\treceived: value,\n\t\t\texpected: \"Integer >= 1\",\n\t\t\tsuggestion: \"Provide a positive integer for page (e.g., page=1)\",\n\t\t\tcontext: {\n\t\t\t\tparameter: \"page\",\n\t\t\t\tminValue: 1,\n\t\t\t\t...context,\n\t\t\t},\n\t\t});\n\t},\n\n\tinvalidPageSize(\n\t\tvalue: string | number | readonly string[] | undefined,\n\t\tpath: string[],\n\t\tcontext?: Partial<PaginationErrorContext>,\n\t) {\n\t\tthrow new ParserError(`Invalid pageSize value: \"${value}\" (must be >= 1)`, {\n\t\t\tcode: \"INVALID_PAGINATION\",\n\t\t\tparser: \"pagination\",\n\t\t\tlocation: buildErrorLocation([\"pagination\", ...path]),\n\t\t\treceived: value,\n\t\t\texpected: \"Integer >= 1\",\n\t\t\tsuggestion: \"Provide a positive integer for pageSize (e.g., pageSize=25)\",\n\t\t\tcontext: {\n\t\t\t\tparameter: \"pageSize\",\n\t\t\t\tminValue: 1,\n\t\t\t\t...context,\n\t\t\t},\n\t\t});\n\t},\n\n\tmaxPageSizeExceeded(value: number, max: number, path: string[]) {\n\t\tthrow new ParserError(`Page size exceeds maximum (${max})`, {\n\t\t\tcode: \"MAX_VALUE_VIOLATION\",\n\t\t\tparser: \"pagination\",\n\t\t\tlocation: buildErrorLocation([\"pagination\", ...path]),\n\t\t\treceived: value,\n\t\t\texpected: `Maximum: ${max}`,\n\t\t\tsuggestion: `Reduce pageSize to ${max} or less`,\n\t\t\tcontext: {\n\t\t\t\tparameter: \"pageSize\",\n\t\t\t\tmaxValue: max,\n\t\t\t},\n\t\t});\n\t},\n\n\tmaxLimitExceeded(value: number, max: number, path: string[]) {\n\t\tthrow new ParserError(`Limit exceeds maximum page size (${max})`, {\n\t\t\tcode: \"MAX_VALUE_VIOLATION\",\n\t\t\tparser: \"pagination\",\n\t\t\tlocation: buildErrorLocation([\"pagination\", ...path]),\n\t\t\treceived: value,\n\t\t\texpected: `Maximum: ${max}`,\n\t\t\tsuggestion: `Reduce limit to ${max} or less`,\n\t\t\tcontext: {\n\t\t\t\tparameter: \"limit\",\n\t\t\t\tmaxValue: max,\n\t\t\t},\n\t\t});\n\t},\n\n\tmaxPageNumberExceeded(value: number, max: number, path: string[]) {\n\t\tthrow new ParserError(`Page number exceeds maximum (${max})`, {\n\t\t\tcode: \"PAGE_OUT_OF_RANGE\",\n\t\t\tparser: \"pagination\",\n\t\t\tlocation: buildErrorLocation([\"pagination\", ...path]),\n\t\t\treceived: value,\n\t\t\texpected: `Maximum: ${max}`,\n\t\t\tsuggestion: `Use page number ${max} or less`,\n\t\t\tcontext: {\n\t\t\t\tparameter: \"page\",\n\t\t\t\tmaxValue: max,\n\t\t\t},\n\t\t});\n\t},\n};\n\n/**\n * Sort Parser Errors\n */\nexport const sortError = {\n\temptyValue(path: string[]) {\n\t\tthrow new ParserError(\"Sort value cannot be empty\", {\n\t\t\tcode: \"EMPTY_VALUE\",\n\t\t\tparser: \"sort\",\n\t\t\tlocation: buildErrorLocation([\"sort\", ...path]),\n\t\t\treceived: \"empty string\",\n\t\t\texpected: \"Field name(s) with optional direction\",\n\t\t\tsuggestion:\n\t\t\t\t\"Provide field names (e.g., 'name' or '-createdAt' for descending)\",\n\t\t\tcontext: {},\n\t\t});\n\t},\n\n\tinvalidFieldName(\n\t\tfield: string,\n\t\tpath: string[],\n\t\tcontext?: Partial<SortErrorContext>,\n\t) {\n\t\tconst reasonDetail = context?.fieldValidationReason\n\t\t\t? ` (Reason: ${context.fieldValidationReason})`\n\t\t\t: \"\";\n\n\t\tthrow new ParserError(`Invalid sort field: ${field}${reasonDetail}`, {\n\t\t\tcode: \"INVALID_FIELD_NAME\",\n\t\t\tparser: \"sort\",\n\t\t\tlocation: buildErrorLocation([\"sort\", ...path]),\n\t\t\treceived: field,\n\t\t\texpected:\n\t\t\t\t\"Field name must start with letter/underscore and contain only alphanumeric characters, underscores, and dots. Use '-' prefix for descending order.\",\n\t\t\tsuggestion:\n\t\t\t\t\"Use valid field names (e.g., 'name', '-createdAt', 'user.age')\",\n\t\t\tcontext: {\n\t\t\t\tsortField: field,\n\t\t\t\tparameter: \"sort\",\n\t\t\t\t...context,\n\t\t\t},\n\t\t});\n\t},\n};\n","/**\n * Where Parser\n *\n * Parses where query params into WhereClause.\n * Examples:\n * ?where[status]=active\n * ?where[price][$gt]=100\n * ?where[name][$contains]=john\n */\n\nimport type { FallbackWhereClause } from \"@datrix/core\";\nimport type { RawQueryParams } from \"@datrix/core\";\nimport {\n\tvalidateFieldName,\n\tisValidWhereOperator,\n\tisLogicalOperator,\n\tgetOperatorValueType,\n} from \"@datrix/core\";\nimport { whereError } from \"./errors\";\n\n/**\n * Parse where parameter\n * Throws ParserError on validation failure\n *\n * @param params - Raw query parameters\n * @returns WhereClause or undefined\n * @throws {ParserError} When validation fails\n */\nexport function parseWhere(\n\tparams: RawQueryParams,\n): FallbackWhereClause | undefined {\n\tconst whereClause: Record<string, unknown> = {};\n\n\t// Find all where[...] parameters\n\tfor (const [key, value] of Object.entries(params)) {\n\t\tif (!key.startsWith(\"where[\")) {\n\t\t\tcontinue;\n\t\t}\n\n\t\t// Extract path: where[a][b][c] -> [\"a\", \"b\", \"c\"]\n\t\tconst parts = key\n\t\t\t.slice(5)\n\t\t\t.split(\"]\")\n\t\t\t.filter((p) => p.startsWith(\"[\"))\n\t\t\t.map((p) => p.slice(1));\n\t\tif (parts.length === 0) continue;\n\n\t\t// Validate parts (field names and operators)\n\t\tfor (let i = 0; i < parts.length; i++) {\n\t\t\tconst part = parts[i]!;\n\n\t\t\t// Check if it's an operator (starts with $)\n\t\t\tif (part.startsWith(\"$\")) {\n\t\t\t\t// Validate operator\n\t\t\t\tif (!isValidWhereOperator(part)) {\n\t\t\t\t\twhereError.invalidOperator(part, parts.slice(0, i), {\n\t\t\t\t\t\toperatorPath: key,\n\t\t\t\t\t});\n\t\t\t\t}\n\n\t\t\t\t// Operators cannot be at the start (i=0) unless they are logical operators\n\t\t\t\tif (i === 0 && !isLogicalOperator(part)) {\n\t\t\t\t\twhereError.invalidFieldName(part, [], {\n\t\t\t\t\t\tfieldValidationReason: \"INVALID_FORMAT\",\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t} else if (/^\\d+$/.test(part)) {\n\t\t\t\t// It's a numeric index - validate context\n\t\t\t\t// Index can only appear after logical operators ($or, $and)\n\t\t\t\tif (i === 0) {\n\t\t\t\t\twhereError.arrayIndexAtStart(part, []);\n\t\t\t\t}\n\n\t\t\t\tconst previousPart = parts[i - 1]!;\n\t\t\t\tif (![\"$or\", \"$and\", \"$in\", \"$nin\"].includes(previousPart)) {\n\t\t\t\t\twhereError.invalidArrayIndex(part, previousPart, parts.slice(0, i), {\n\t\t\t\t\t\tpreviousOperator: previousPart,\n\t\t\t\t\t\toperatorPath: key,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// It's a field name - validate it\n\t\t\t\tconst validation = validateFieldName(part);\n\t\t\t\tif (!validation.valid) {\n\t\t\t\t\twhereError.invalidFieldName(part, parts.slice(0, i), {\n\t\t\t\t\t\tfieldValidationReason: validation.reason,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Build the nested structure\n\t\tlet current = whereClause;\n\t\tconst pathParts = [...parts];\n\t\tfor (let i = 0; i < pathParts.length; i++) {\n\t\t\tconst part = pathParts[i]!;\n\t\t\tconst isLast = i === pathParts.length - 1;\n\n\t\t\tif (isLast) {\n\t\t\t\t// Find operator context for proper value parsing\n\t\t\t\t// Only use operator context for STRING operators (not array operators like $in, $nin)\n\t\t\t\t// Array operators' elements should be parsed normally (as numbers, strings, etc.)\n\t\t\t\tlet operatorContext: string | undefined;\n\t\t\t\tconst isArrayIndex = /^\\d+$/.test(part);\n\n\t\t\t\tif (part.startsWith(\"$\")) {\n\t\t\t\t\t// Current part is the operator: where[field][$op]=value\n\t\t\t\t\tconst expectedType = getOperatorValueType(part);\n\t\t\t\t\t// Only set context for string operators (to prevent number coercion)\n\t\t\t\t\tif (expectedType === \"string\") {\n\t\t\t\t\t\toperatorContext = part;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t// Note: For array indices (e.g., $in[0]), we don't set operatorContext\n\t\t\t\t// because array elements should be parsed as their natural types\n\n\t\t\t\t// Parse the value with operator context\n\t\t\t\tconst parsedValue = parseValue(value, operatorContext);\n\n\t\t\t\t// Validate operator value type only when operator itself is the last part\n\t\t\t\t// (not for array indices like $in[0], $nin[1])\n\t\t\t\tif (part.startsWith(\"$\") && !isArrayIndex) {\n\t\t\t\t\tvalidateOperatorValue(part, parsedValue, pathParts);\n\t\t\t\t}\n\n\t\t\t\tcurrent[part] = parsedValue;\n\t\t\t} else {\n\t\t\t\tif (current[part] === undefined) {\n\t\t\t\t\tcurrent[part] = {};\n\t\t\t\t}\n\t\t\t\tcurrent = current[part] as Record<string, unknown>;\n\t\t\t}\n\t\t}\n\t}\n\n\t// Transform into Final WhereClause\n\tconst transformResult = transformToFinalWhere(whereClause);\n\tconst finalClause = transformResult as FallbackWhereClause;\n\n\t// If no where parameters found, return undefined\n\tif (Object.keys(finalClause).length === 0) {\n\t\treturn undefined;\n\t}\n\n\t// Validate nesting depth\n\tvalidateNestingDepth(finalClause);\n\n\treturn finalClause;\n}\n\n/**\n * Post-process the object to handle logical operators which should be arrays\n */\nfunction transformToFinalWhere(obj: unknown): unknown {\n\tif (obj === null || typeof obj !== \"object\" || Array.isArray(obj)) {\n\t\treturn obj;\n\t}\n\n\tconst typedObj = obj as Record<string, unknown>;\n\tconst result: Record<string, unknown> = {};\n\n\tfor (const [key, value] of Object.entries(typedObj)) {\n\t\t// Operators that require array transformation\n\t\t// NOTE: $not is NOT an array operator - it takes a single object, not an array\n\t\tconst arrayOperators = [\"$or\", \"$and\", \"$in\", \"$nin\"];\n\n\t\tif (arrayOperators.includes(key)) {\n\t\t\t// Transform object with numeric keys into array\n\t\t\tif (\n\t\t\t\ttypeof value === \"object\" &&\n\t\t\t\tvalue !== null &&\n\t\t\t\t!Array.isArray(value)\n\t\t\t) {\n\t\t\t\tconst valueObj = value as Record<string, unknown>;\n\t\t\t\tconst keys = Object.keys(valueObj);\n\n\t\t\t\t// Validate that all keys are numeric\n\t\t\t\tconst numericKeys: number[] = [];\n\t\t\t\tfor (const k of keys) {\n\t\t\t\t\tconst num = Number(k);\n\t\t\t\t\tif (isNaN(num) || !Number.isInteger(num) || num < 0) {\n\t\t\t\t\t\twhereError.invalidArrayIndexFormat(k, key, [key]);\n\t\t\t\t\t}\n\t\t\t\t\tnumericKeys.push(num);\n\t\t\t\t}\n\n\t\t\t\t// Sort and validate consecutive sequence starting from 0\n\t\t\t\tconst sortedKeys = numericKeys.sort((a, b) => a - b);\n\n\t\t\t\tif (sortedKeys.length > 0 && sortedKeys[0] !== 0) {\n\t\t\t\t\twhereError.arrayIndexNotStartingFromZero(sortedKeys[0]!, key, [key]);\n\t\t\t\t}\n\n\t\t\t\tfor (let i = 0; i < sortedKeys.length; i++) {\n\t\t\t\t\tif (sortedKeys[i] !== i) {\n\t\t\t\t\t\twhereError.arrayIndexNotConsecutive(i, key, [key], sortedKeys);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// For $in/$nin, values are primitives - don't recursively transform\n\t\t\t\t// For $or/$and, values are conditions - recursively transform\n\t\t\t\tif ([\"$in\", \"$nin\"].includes(key)) {\n\t\t\t\t\tresult[key] = sortedKeys.map((idx) => valueObj[String(idx)]);\n\t\t\t\t} else {\n\t\t\t\t\tconst transformed: unknown[] = [];\n\t\t\t\t\tfor (const idx of sortedKeys) {\n\t\t\t\t\t\tconst transformResult = transformToFinalWhere(\n\t\t\t\t\t\t\tvalueObj[String(idx)],\n\t\t\t\t\t\t);\n\t\t\t\t\t\ttransformed.push(transformResult);\n\t\t\t\t\t}\n\t\t\t\t\tresult[key] = transformed;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tconst transformResult = transformToFinalWhere(value);\n\t\t\t\tresult[key] = transformResult;\n\t\t\t}\n\t\t} else {\n\t\t\tconst transformResult = transformToFinalWhere(value);\n\t\t\tresult[key] = transformResult;\n\t\t}\n\t}\n\n\treturn result;\n}\n\n/**\n * Parse value from string/array\n * Handles: strings, numbers, booleans, null, arrays (for $in, $nin)\n *\n * @param value - The raw value to parse\n * @param operator - Optional operator context for type-aware parsing\n */\nfunction parseValue(\n\tvalue: string | readonly string[] | undefined,\n\toperator?: string,\n): unknown {\n\tif (value === undefined) {\n\t\treturn undefined;\n\t}\n\n\t// Handle array (for $in, $nin operators)\n\tif (Array.isArray(value)) {\n\t\tconst parsed: unknown[] = [];\n\t\tfor (const v of value) {\n\t\t\tif (typeof v === \"string\") {\n\t\t\t\tconst result = parseSingleValue(v, operator);\n\t\t\t\tparsed.push(result);\n\t\t\t} else {\n\t\t\t\tparsed.push(v);\n\t\t\t}\n\t\t}\n\t\treturn parsed;\n\t}\n\n\tif (typeof value === \"string\") {\n\t\treturn parseSingleValue(value, operator);\n\t}\n\n\treturn value;\n}\n\n/**\n * Parse a single value from string\n * Returns Result to handle validation errors\n *\n * @param value - The raw string value to parse\n * @param operator - Optional operator context for type-aware parsing\n */\nfunction parseSingleValue(value: string, operator?: string): unknown {\n\t// Import MAX_WHERE_VALUE_LENGTH\n\tconst MAX_WHERE_VALUE_LENGTH = 1000;\n\n\t// Check value length first - reject instead of truncate\n\tif (value.length > MAX_WHERE_VALUE_LENGTH) {\n\t\twhereError.maxValueLength(value.length, []);\n\t}\n\n\t// If operator expects string, return as-is (no type coercion)\n\tif (operator) {\n\t\tconst expectedType = getOperatorValueType(operator);\n\t\tif (expectedType === \"string\") {\n\t\t\treturn value;\n\t\t}\n\t}\n\n\t// Handle special values\n\tif (value === \"null\") {\n\t\treturn null;\n\t}\n\n\tif (value === \"true\") {\n\t\treturn true;\n\t}\n\n\tif (value === \"false\") {\n\t\treturn false;\n\t}\n\n\t/*\n\t// Try to parse as number\n\tconst num = Number(value);\n\tif (!isNaN(num) && value.trim() !== '') {\n\t\treturn num;\n\t}\n\t*/\n\n\t// Return as string\n\treturn value;\n}\n\n/**\n * Validate operator value type\n */\nfunction validateOperatorValue(\n\toperator: string,\n\tvalue: unknown,\n\tpath: string[],\n): void {\n\tconst expectedType = getOperatorValueType(operator);\n\n\tif (!expectedType) {\n\t\t// Unknown operator (shouldn't happen, already validated)\n\t\treturn;\n\t}\n\n\t// Check type-specific requirements\n\tif (expectedType === \"array\") {\n\t\tif (!Array.isArray(value)) {\n\t\t\twhereError.invalidOperatorValue(operator, typeof value, path, value);\n\t\t}\n\n\t\t// Check if array is empty\n\t\tif ((value as []).length === 0) {\n\t\t\twhereError.emptyArrayOperator(operator, path);\n\t\t}\n\t}\n}\n\n/**\n * Validate nesting depth for logical operators\n */\nfunction validateNestingDepth(\n\tclause: FallbackWhereClause,\n\tdepth: number = 0,\n\tpath: string[] = [],\n): void {\n\tconst MAX_LOGICAL_NESTING_DEPTH = 10;\n\n\tif (depth > MAX_LOGICAL_NESTING_DEPTH) {\n\t\twhereError.maxDepthExceeded(depth, path);\n\t}\n\n\t// Check nested logical operators\n\tfor (const [key, value] of Object.entries(clause)) {\n\t\tif (isLogicalOperator(key) && Array.isArray(value)) {\n\t\t\t// Validate that logical operators have array of conditions\n\t\t\tif (value.length === 0) {\n\t\t\t\twhereError.emptyLogicalOperator(key, [...path, key]);\n\t\t\t}\n\n\t\t\t// Recursively check each condition\n\t\t\tfor (const condition of value) {\n\t\t\t\tif (typeof condition === \"object\" && condition !== null) {\n\t\t\t\t\tvalidateNestingDepth(condition as FallbackWhereClause, depth + 1, [\n\t\t\t\t\t\t...path,\n\t\t\t\t\t\tkey,\n\t\t\t\t\t]);\n\t\t\t\t}\n\t\t\t}\n\t\t} else if (\n\t\t\ttypeof value === \"object\" &&\n\t\t\tvalue !== null &&\n\t\t\t!Array.isArray(value)\n\t\t) {\n\t\t\t// Recursively check nested objects\n\t\t\tvalidateNestingDepth(value as FallbackWhereClause, depth, [...path, key]);\n\t\t}\n\t}\n}\n","/**\n * Populate Parser\n *\n * Parses populate query params into PopulateClause.\n * Examples:\n * ?populate=* -> populate all relations\n * ?populate[profile]=* -> populate profile with all fields\n * ?populate[profile][fields][0]=name -> populate profile with specific fields\n * ?populate[posts][populate][comments]=* -> nested populate\n */\n\nimport type { RawQueryParams } from \"@datrix/core\";\nimport { DatrixRecord } from \"@datrix/core\";\nimport { PopulateClause, PopulateOptions } from \"@datrix/core\";\nimport { validateFieldName } from \"@datrix/core\";\nimport { populateError } from \"./errors\";\n\n/**\n * Default max populate depth\n */\nconst DEFAULT_MAX_DEPTH = 5;\n\n/**\n * Parse populate parameter\n * Throws ParserError on validation failure\n *\n * @param params - Raw query parameters\n * @param maxDepth - Maximum nesting depth (default: 5)\n * @returns PopulateClause or undefined\n * @throws {ParserError} When validation fails\n */\nexport function parsePopulate(\n\tparams: RawQueryParams,\n\tmaxDepth: number = DEFAULT_MAX_DEPTH,\n): PopulateClause<DatrixRecord> | undefined {\n\t// Validate maxDepth\n\tif (maxDepth <= 0) {\n\t\tpopulateError.maxDepthExceeded(maxDepth, maxDepth, [\"config\"], {\n\t\t\tmaxDepth,\n\t\t});\n\t}\n\n\t// Build populate clause\n\tconst populateClause:\n\t\t| Record<string, PopulateOptions<DatrixRecord> | \"*\">\n\t\t| true = {};\n\n\t// Check for simple populate parameter (string)\n\tconst mainPopulate = params[\"populate\"];\n\tif (mainPopulate !== undefined) {\n\t\tif (mainPopulate === \"*\") {\n\t\t\treturn \"*\";\n\t\t}\n\n\t\tif (mainPopulate === \"true\") {\n\t\t\treturn true;\n\t\t}\n\n\t\tif (typeof mainPopulate === \"string\") {\n\t\t\t// Handle empty or whitespace-only string\n\t\t\tconst trimmed = mainPopulate.trim();\n\t\t\tif (trimmed === \"\") {\n\t\t\t\tpopulateError.emptyValue([]);\n\t\t\t}\n\n\t\t\t// Single relation: populate=author\n\t\t\tconst validation = validateFieldName(trimmed);\n\t\t\tif (!validation.valid) {\n\t\t\t\tpopulateError.invalidRelation(trimmed, [trimmed], {\n\t\t\t\t\tfieldValidationReason: validation.reason,\n\t\t\t\t});\n\t\t\t}\n\t\t\tpopulateClause[trimmed] = \"*\";\n\t\t} else if (Array.isArray(mainPopulate)) {\n\t\t\t// Handle array: populate[]=author&populate[]=comments\n\t\t\tfor (const rel of mainPopulate) {\n\t\t\t\tif (rel && typeof rel === \"string\") {\n\t\t\t\t\tconst trimmed = rel.trim();\n\t\t\t\t\t// Validate relation name\n\t\t\t\t\tconst validation = validateFieldName(trimmed);\n\t\t\t\t\tif (!validation.valid) {\n\t\t\t\t\t\tpopulateError.invalidRelation(trimmed, [trimmed], {\n\t\t\t\t\t\t\tfieldValidationReason: validation.reason,\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t\tpopulateClause[trimmed] = \"*\";\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\t// Invalid type (number, object, etc.)\n\t\t\tpopulateError.invalidType(typeof mainPopulate, []);\n\t\t}\n\t}\n\n\t// Extract all populate parameters\n\tconst populateParams = extractPopulateParams(params);\n\n\t// Detect if this is an indexed array format: populate[0]=author&populate[1]=comments\n\tconst indexedArrayRelations: string[] = [];\n\tfor (const [key, value] of Object.entries(params)) {\n\t\tconst indexMatch = key.match(/^populate\\[(\\d+)\\]$/);\n\t\tif (indexMatch && typeof value === \"string\") {\n\t\t\tconst index = Number(indexMatch[1]);\n\t\t\tconst relationName = value.trim();\n\n\t\t\t// Validate relation name\n\t\t\tconst validation = validateFieldName(relationName);\n\t\t\tif (!validation.valid) {\n\t\t\t\tpopulateError.invalidRelation(relationName, [relationName], {\n\t\t\t\t\tfieldValidationReason: validation.reason,\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tindexedArrayRelations[index] = relationName;\n\t\t}\n\t}\n\n\t// If we found indexed array format, return as string[]\n\tif (indexedArrayRelations.length > 0) {\n\t\treturn indexedArrayRelations.filter(\n\t\t\tBoolean,\n\t\t) as unknown as PopulateClause<DatrixRecord>; // Remove empty slots\n\t}\n\n\t// Parse each relation (object format)\n\tfor (const [relation, relationParams] of Object.entries(populateParams)) {\n\t\tconst parseResult = parseRelation(relation, relationParams, 1, maxDepth);\n\t\tpopulateClause[relation] = parseResult;\n\t}\n\n\t// If no populate parameters found at all, return undefined\n\tif (Object.keys(populateClause).length === 0) {\n\t\treturn undefined;\n\t}\n\n\treturn populateClause;\n}\n\n/**\n * Relation parameters extracted from query\n */\ninterface RelationParams {\n\treadonly fields?: readonly string[];\n\treadonly populate?: Record<string, RelationParams>;\n\treadonly isWildcard?: boolean;\n}\n\n/**\n * Extract populate parameters grouped by relation\n */\nfunction extractPopulateParams(\n\tparams: RawQueryParams,\n): Record<string, RelationParams> {\n\tconst relations: Record<string, Record<string, unknown>> = {};\n\n\tfor (const [key, value] of Object.entries(params)) {\n\t\tif (!key.startsWith(\"populate[\")) {\n\t\t\tcontinue;\n\t\t}\n\n\t\t// Parse: populate[relation][...][...]\n\t\tconst parts = key.match(/populate\\[([^\\]]+)\\](.*)$/);\n\t\tif (!parts) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst relation = parts[1];\n\t\tconst rest = parts[2];\n\n\t\tif (!relation) {\n\t\t\tcontinue;\n\t\t}\n\n\t\t// Initialize relation if not exists\n\t\tif (relations[relation] === undefined) {\n\t\t\trelations[relation] = {};\n\t\t}\n\n\t\t// Check if this is a wildcard: populate[relation]=*\n\t\tif (rest === \"\" && value === \"*\") {\n\t\t\trelations[relation][\"isWildcard\"] = true;\n\t\t\tcontinue;\n\t\t}\n\n\t\t// Parse remaining path: [fields][0], [populate][comments], etc.\n\t\tif (rest !== undefined) {\n\t\t\tparseRelationPath(relations[relation], rest, value);\n\t\t}\n\t}\n\n\treturn relations;\n}\n\n/**\n * Parse the path within a relation\n * Examples:\n * [fields][0] -> add to fields array\n * [populate][comments] -> nested populate\n */\nfunction parseRelationPath(\n\trelationData: Record<string, unknown>,\n\tpath: string,\n\tvalue: string | readonly string[] | undefined,\n): void {\n\tif (path === \"\") {\n\t\treturn;\n\t}\n\n\t// Match [key][...rest]\n\tconst match = path.match(/^\\[([^\\]]+)\\](.*)$/);\n\tif (!match) {\n\t\treturn;\n\t}\n\n\tconst key = match[1];\n\tconst rest = match[2];\n\n\tif (key === \"fields\") {\n\t\t// Handle fields array\n\t\tif (relationData[\"fields\"] === undefined) {\n\t\t\trelationData[\"fields\"] = [];\n\t\t}\n\n\t\tconst fieldsArray = Array.isArray(relationData[\"fields\"])\n\t\t\t? relationData[\"fields\"]\n\t\t\t: [];\n\n\t\tif (rest === \"\") {\n\t\t\t// populate[relation][fields]=* or comma-separated\n\t\t\tif (value === \"*\") {\n\t\t\t\trelationData[\"fields\"] = \"*\";\n\t\t\t} else if (typeof value === \"string\") {\n\t\t\t\tconst fields = value.split(\",\").map((f) => f.trim());\n\t\t\t\t// Validate each field\n\t\t\t\tfor (const field of fields) {\n\t\t\t\t\tconst validation = validateFieldName(field);\n\t\t\t\t\tif (!validation.valid) {\n\t\t\t\t\t\tpopulateError.invalidFieldName(field, [\"fields\"], {\n\t\t\t\t\t\t\tfieldValidationReason: validation.reason,\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tfieldsArray.push(...fields);\n\t\t\t}\n\t\t} else if (rest !== undefined) {\n\t\t\t// populate[relation][fields][0]=name\n\t\t\tconst indexMatch = rest.match(/^\\[(\\d+)\\]$/);\n\t\t\tif (indexMatch && typeof value === \"string\") {\n\t\t\t\tconst field = value.trim();\n\t\t\t\t// Validate field name\n\t\t\t\tconst validation = validateFieldName(field);\n\t\t\t\tif (!validation.valid) {\n\t\t\t\t\tpopulateError.invalidFieldName(field, [\"fields\"], {\n\t\t\t\t\t\tfieldValidationReason: validation.reason,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\tfieldsArray.push(field);\n\t\t\t}\n\t\t}\n\t} else if (key === \"populate\") {\n\t\t// Handle nested populate\n\t\tif (relationData[\"populate\"] === undefined) {\n\t\t\trelationData[\"populate\"] = {};\n\t\t}\n\n\t\tconst populateObj =\n\t\t\ttypeof relationData[\"populate\"] === \"object\" &&\n\t\t\t\t!Array.isArray(relationData[\"populate\"])\n\t\t\t\t? (relationData[\"populate\"] as Record<string, Record<string, unknown>>)\n\t\t\t\t: {};\n\n\t\trelationData[\"populate\"] = populateObj;\n\n\t\t// Handle instructions for the current relation's populates\n\t\tif (rest === \"\") {\n\t\t\tif (value === \"*\") {\n\t\t\t\t// populate[relation][populate]=*\n\t\t\t\trelationData[\"isWildcard\"] = true;\n\t\t\t} else if (typeof value === \"string\") {\n\t\t\t\t// populate[relation][populate]=profile,comments\n\t\t\t\tconst relations = value\n\t\t\t\t\t.split(\",\")\n\t\t\t\t\t.map((r) => r.trim())\n\t\t\t\t\t.filter(Boolean);\n\t\t\t\tfor (const rel of relations) {\n\t\t\t\t\tif (rel === \"*\") {\n\t\t\t\t\t\trelationData[\"isWildcard\"] = true;\n\t\t\t\t\t} else if (populateObj[rel] === undefined) {\n\t\t\t\t\t\tpopulateObj[rel] = { isWildcard: true };\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t} else if (rest !== undefined) {\n\t\t\t// populate[relation][populate][nestedRelation]...\n\t\t\tconst nestedMatch = rest.match(/^\\[([^\\]]+)\\](.*)$/);\n\t\t\tif (nestedMatch) {\n\t\t\t\tconst nestedRelation = nestedMatch[1];\n\t\t\t\tconst nestedRest = nestedMatch[2];\n\n\t\t\t\tif (nestedRelation) {\n\t\t\t\t\tif (populateObj[nestedRelation] === undefined) {\n\t\t\t\t\t\tpopulateObj[nestedRelation] = {};\n\t\t\t\t\t}\n\n\t\t\t\t\tif (nestedRest === \"\" && value === \"*\") {\n\t\t\t\t\t\tpopulateObj[nestedRelation][\"isWildcard\"] = true;\n\t\t\t\t\t} else if (nestedRest !== undefined) {\n\t\t\t\t\t\tparseRelationPath(populateObj[nestedRelation], nestedRest, value);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\n/**\n * Parse a single relation into PopulateOptions\n */\nfunction parseRelation(\n\trelation: string,\n\tparams: RelationParams,\n\tcurrentDepth: number,\n\tmaxDepth: number,\n\tpath: string[] = [],\n): PopulateOptions<DatrixRecord> | \"*\" {\n\t// Validate relation name\n\tconst validation = validateFieldName(relation);\n\tif (!validation.valid) {\n\t\tpopulateError.invalidRelation(relation, [...path, relation], {\n\t\t\trelationPath: [...path, relation].join(\".\"),\n\t\t\tfieldValidationReason: validation.reason,\n\t\t});\n\t}\n\n\t// Check depth\n\tif (currentDepth > maxDepth) {\n\t\tpopulateError.maxDepthExceeded(\n\t\t\tcurrentDepth,\n\t\t\tmaxDepth,\n\t\t\t[...path, relation],\n\t\t\t{\n\t\t\t\trelation,\n\t\t\t\trelationPath: [...path, relation].join(\".\"),\n\t\t\t\tcurrentDepth,\n\t\t\t\tnestedRelations: [...path, relation],\n\t\t\t},\n\t\t);\n\t}\n\n\t// Handle wildcard\n\tif (params.isWildcard) {\n\t\treturn \"*\";\n\t}\n\n\tconst options: Record<string, unknown> = {};\n\n\t// Add fields if specified\n\tif (params.fields !== undefined) {\n\t\tif (typeof params.fields === \"string\" && params.fields === \"*\") {\n\t\t\toptions[\"select\"] = \"*\";\n\t\t} else if (Array.isArray(params.fields) && params.fields.length > 0) {\n\t\t\toptions[\"select\"] = params.fields;\n\t\t}\n\t}\n\n\t// Add nested populates\n\tif (params.populate !== undefined) {\n\t\tconst nestedPopulate: Record<string, PopulateOptions<DatrixRecord> | \"*\"> =\n\t\t\t{};\n\n\t\tfor (const [nestedRelation, nestedParams] of Object.entries(\n\t\t\tparams.populate,\n\t\t)) {\n\t\t\tnestedPopulate[nestedRelation] = parseRelation(\n\t\t\t\tnestedRelation,\n\t\t\t\tnestedParams,\n\t\t\t\tcurrentDepth + 1,\n\t\t\t\tmaxDepth,\n\t\t\t\t[...path, relation],\n\t\t\t);\n\t\t}\n\n\t\tif (Object.keys(nestedPopulate).length > 0) {\n\t\t\toptions[\"populate\"] = nestedPopulate;\n\t\t}\n\t}\n\n\t// If no options specified, return wildcard\n\tif (Object.keys(options).length === 0) {\n\t\treturn \"*\";\n\t}\n\n\treturn options as PopulateOptions<DatrixRecord>;\n}\n","/**\n * Context Builder Middleware\n *\n * Builds unified request context from raw request\n * This is the SINGLE PLACE where all request preprocessing happens\n */\n\nimport type {\n\tRequestContext,\n\tHttpMethod,\n\tContextBuilderOptions,\n} from \"./types\";\nimport type { Datrix } from \"@datrix/core\";\nimport { ParserError } from \"@datrix/core\";\nimport { methodToAction } from \"./permission\";\nimport { parseQuery } from \"../parser\";\nimport { FallbackInput } from \"@datrix/core\";\nimport { AuthUser, IApiPlugin } from \"@datrix/core\";\n\n/**\n * Extract table name from URL path\n * /api/users -> 'users'\n * /api/users/123 -> 'users'\n */\nfunction extractTableNameFromPath(\n\tpathname: string,\n\tprefix: string,\n): string | null {\n\tconst segments = pathname.split(\"/\").filter(Boolean);\n\tconst prefixSegments = prefix.split(\"/\").filter(Boolean);\n\tconst pathSegments = segments.slice(prefixSegments.length);\n\n\tif (pathSegments.length === 0) {\n\t\treturn null;\n\t}\n\n\treturn pathSegments[0] ?? null;\n}\n\n/**\n * Extract record ID from URL path\n * /api/user/123 -> '123'\n * /api/user -> null\n */\nfunction extractIdFromPath(pathname: string, prefix: string): number | null {\n\tconst segments = pathname.split(\"/\").filter(Boolean);\n\tconst prefixSegments = prefix.split(\"/\").filter(Boolean);\n\tconst pathSegments = segments.slice(prefixSegments.length);\n\n\tif (pathSegments.length < 2) {\n\t\treturn null;\n\t}\n\tconst val = parseInt(pathSegments[1]!, 10);\n\treturn isNaN(val) ? null : val;\n}\n\n/**\n * Parser error wrapper for context building\n */\nexport class ContextBuildError extends Error {\n\treadonly parserError: ParserError;\n\n\tconstructor(parserError: ParserError) {\n\t\tsuper(parserError.message);\n\t\tthis.name = \"ContextBuildError\";\n\t\tthis.parserError = parserError;\n\t}\n}\n\n/**\n * Build Request Context\n *\n * This is the CENTRALIZED place where:\n * 1. Schema resolution happens\n * 2. Authentication happens (only if enabled)\n * 3. URL parsing happens\n * 4. Query parsing happens\n * 5. Body parsing happens\n *\n * ALL requests go through this function ONCE\n *\n * @throws {ContextBuildError} When query parsing fails\n */\nexport async function buildRequestContext<TRole extends string = string>(\n\trequest: Request,\n\tdatrix: Datrix,\n\tapi: IApiPlugin<TRole>,\n\toptions: ContextBuilderOptions = {},\n): Promise<RequestContext<TRole>> {\n\tconst apiPrefix = options.apiPrefix ?? \"/api\";\n\tconst url = new URL(request.url);\n\tconst method = request.method as HttpMethod;\n\tconst authEnabled = api.isAuthEnabled();\n\n\t// 1. RESOLVE SCHEMA from URL\n\tconst tableName = extractTableNameFromPath(url.pathname, apiPrefix);\n\tconst modelName =\n\t\ttableName === \"upload\" && api.upload\n\t\t\t? api.upload.getModelName()\n\t\t\t: datrix.getSchemas().findModelByTableName(tableName);\n\tconst schema = modelName ? (datrix.getSchema(modelName) ?? null) : null;\n\n\t// 2. DERIVE ACTION from HTTP method\n\tconst action = methodToAction(method);\n\n\t// 3. EXTRACT ID from URL\n\tconst id = extractIdFromPath(url.pathname, apiPrefix);\n\n\t// 4. AUTHENTICATE (only if auth is enabled)\n\tlet user: AuthUser | null = null;\n\tif (authEnabled && api.authManager) {\n\t\tconst authResult = await api.authManager.authenticate(request);\n\t\tuser = authResult?.user ?? null;\n\t}\n\n\t// 5. PARSE QUERY (from query string - works for all HTTP methods)\n\tlet query = null;\n\tconst queryParams: Record<string, string | string[]> = {};\n\turl.searchParams.forEach((value, key) => {\n\t\tconst existing = queryParams[key];\n\t\tif (existing !== undefined) {\n\t\t\tif (Array.isArray(existing)) {\n\t\t\t\texisting.push(value);\n\t\t\t} else {\n\t\t\t\tqueryParams[key] = [existing, value];\n\t\t\t}\n\t\t} else {\n\t\t\tqueryParams[key] = value;\n\t\t}\n\t});\n\n\tif (Object.keys(queryParams).length > 0) {\n\t\tquery = parseQuery(queryParams);\n\t}\n\n\t// 6. PARSE BODY (for POST/PATCH/PUT requests)\n\tlet body = null;\n\tif ([\"POST\", \"PATCH\", \"PUT\"].includes(method)) {\n\t\ttry {\n\t\t\tconst contentType = request.headers.get(\"content-type\");\n\t\t\tif (contentType?.includes(\"application/json\")) {\n\t\t\t\tbody = (await request.json()) as FallbackInput;\n\t\t\t}\n\t\t} catch {\n\t\t\t// Invalid JSON, body stays null\n\t\t}\n\t}\n\n\t// 7. EXTRACT HEADERS\n\tconst headers: Record<string, string> = {};\n\trequest.headers.forEach((value, key) => {\n\t\theaders[key] = value;\n\t});\n\n\t// 8. BUILD UNIFIED CONTEXT\n\treturn {\n\t\tschema,\n\t\taction,\n\t\tid,\n\t\tmethod,\n\t\tquery,\n\t\tbody,\n\t\theaders,\n\t\turl,\n\t\trequest,\n\t\tuser,\n\t\tdatrix,\n\t\tapi,\n\t\tauthEnabled,\n\t};\n}\n","/**\n * Unified Request Handler\n *\n * SINGLE ENTRY POINT for all API requests\n * Handles authentication, permission checking, and routing\n */\n\nimport type { Datrix } from \"@datrix/core\";\nimport type {\n\tRequestContext,\n\tContextBuilderOptions,\n} from \"../middleware/types\";\nimport { buildRequestContext } from \"../middleware/context\";\nimport {\n\tcheckSchemaPermission,\n\tcheckFieldsForWrite,\n\tfilterFieldsForRead,\n\tfilterRecordsForRead,\n} from \"../middleware/permission\";\nimport { jsonResponse, datrixErrorResponse } from \"./utils\";\nimport { handlerError } from \"../errors/api-error\";\nimport { DatrixError, DatrixValidationError } from \"@datrix/core\";\nimport type { DatrixEntry } from \"@datrix/core\";\nimport { ResponseData } from \"@datrix/core\";\nimport { IApiPlugin } from \"@datrix/core\";\n\n/**\n * Handle GET request\n */\nasync function handleGet(ctx: RequestContext): Promise<Response> {\n\tconst { datrix, schema, authEnabled } = ctx;\n\n\tif (!schema) {\n\t\tthrow handlerError.schemaNotFound(ctx.url.pathname);\n\t}\n\n\tconst { upload } = ctx.api;\n\n\tif (ctx.id) {\n\t\tconst result = await datrix.findById(schema.name, ctx.id, {\n\t\t\tselect: ctx.query?.select,\n\t\t\tpopulate: ctx.query?.populate,\n\t\t});\n\n\t\tif (!result) {\n\t\t\tthrow handlerError.recordNotFound(schema.name, ctx.id);\n\t\t}\n\n\t\tif (authEnabled) {\n\t\t\tconst { data: filteredResult } = await filterFieldsForRead(\n\t\t\t\tschema,\n\t\t\t\tresult,\n\t\t\t\tctx,\n\t\t\t);\n\t\t\tconst data = upload\n\t\t\t\t? await upload.injectUrls(filteredResult)\n\t\t\t\t: filteredResult;\n\t\t\treturn jsonResponse({ data });\n\t\t}\n\n\t\tconst data = upload ? await upload.injectUrls(result) : result;\n\t\treturn jsonResponse({ data });\n\t} else {\n\t\tconst page = ctx.query?.page ?? 1;\n\t\tconst pageSize = ctx.query?.pageSize ?? 25;\n\t\tconst limit = pageSize;\n\t\tconst offset = (page - 1) * pageSize;\n\n\t\tconst result = await datrix.findMany(schema.name, {\n\t\t\twhere: ctx.query?.where,\n\t\t\tselect: ctx.query?.select,\n\t\t\tpopulate: ctx.query?.populate,\n\t\t\torderBy: ctx.query?.orderBy,\n\t\t\tlimit,\n\t\t\toffset,\n\t\t});\n\n\t\tconst total = await datrix.count(schema.name, ctx.query?.where);\n\t\tconst totalPages = Math.ceil(total / pageSize);\n\n\t\tif (authEnabled) {\n\t\t\tconst filteredResults = await filterRecordsForRead(schema, result, ctx);\n\t\t\tconst data = upload\n\t\t\t\t? await upload.injectUrls(filteredResults)\n\t\t\t\t: filteredResults;\n\n\t\t\tconst response: ResponseData = {\n\t\t\t\tdata,\n\t\t\t\tmeta: { total, page, pageSize, totalPages },\n\t\t\t};\n\n\t\t\treturn jsonResponse(response);\n\t\t}\n\n\t\tconst data = upload ? await upload.injectUrls(result) : result;\n\t\tconst response: ResponseData = {\n\t\t\tdata,\n\t\t\tmeta: { total, page, pageSize, totalPages },\n\t\t};\n\n\t\treturn jsonResponse(response);\n\t}\n}\n\n/**\n * Handle POST request\n */\nasync function handlePost(ctx: RequestContext): Promise<Response> {\n\tconst { datrix, schema, authEnabled, body, query } = ctx;\n\n\tif (!schema) {\n\t\tthrow handlerError.schemaNotFound(ctx.url.pathname);\n\t}\n\n\tif (!body || typeof body !== \"object\" || Array.isArray(body)) {\n\t\tthrow handlerError.invalidBody();\n\t}\n\n\tif (authEnabled) {\n\t\tconst fieldCheck = await checkFieldsForWrite(schema, ctx);\n\n\t\tif (!fieldCheck.allowed) {\n\t\t\tthrow handlerError.permissionDenied(\n\t\t\t\t`Permission denied for fields: ${fieldCheck.deniedFields?.join(\", \")}`,\n\t\t\t\t{ deniedFields: fieldCheck.deniedFields },\n\t\t\t);\n\t\t}\n\t}\n\n\tconst { upload } = ctx.api;\n\n\tconst result = await datrix.create(schema.name, body, {\n\t\tselect: query?.select,\n\t\tpopulate: query?.populate,\n\t});\n\n\tif (authEnabled) {\n\t\tconst { data: filteredResult } = await filterFieldsForRead(\n\t\t\tschema,\n\t\t\tresult as unknown as DatrixEntry,\n\t\t\tctx,\n\t\t);\n\t\tconst data = upload\n\t\t\t? await upload.injectUrls(filteredResult)\n\t\t\t: filteredResult;\n\t\treturn jsonResponse({ data }, 201);\n\t}\n\n\tconst data = upload ? await upload.injectUrls(result) : result;\n\treturn jsonResponse({ data }, 201);\n}\n\n/**\n * Handle PATCH/PUT request (update)\n */\nasync function handleUpdate(ctx: RequestContext): Promise<Response> {\n\tconst { datrix, schema, authEnabled, body, id } = ctx;\n\n\tif (!schema) {\n\t\tthrow handlerError.schemaNotFound(ctx.url.pathname);\n\t}\n\n\tif (!id) {\n\t\tthrow handlerError.missingId(\"update\");\n\t}\n\n\tif (!body || typeof body !== \"object\" || Array.isArray(body)) {\n\t\tthrow handlerError.invalidBody();\n\t}\n\n\tconst existingRecord = await datrix.findById(schema.name, id);\n\n\tif (!existingRecord) {\n\t\tthrow handlerError.recordNotFound(schema.name, id);\n\t}\n\n\tif (authEnabled) {\n\t\tconst fieldCheck = await checkFieldsForWrite(schema, ctx);\n\n\t\tif (!fieldCheck.allowed) {\n\t\t\tthrow handlerError.permissionDenied(\n\t\t\t\t`Permission denied for fields: ${fieldCheck.deniedFields?.join(\", \")}`,\n\t\t\t\t{ deniedFields: fieldCheck.deniedFields },\n\t\t\t);\n\t\t}\n\t}\n\n\tconst result = await datrix.update(schema.name, id, body, {\n\t\tselect: ctx.query?.select,\n\t\tpopulate: ctx.query?.populate,\n\t});\n\n\tif (!result) {\n\t\tthrow handlerError.recordNotFound(schema.name, id);\n\t}\n\n\tconst { upload } = ctx.api;\n\n\tif (authEnabled) {\n\t\tconst { data: filteredResult } = await filterFieldsForRead(\n\t\t\tschema,\n\t\t\tresult as unknown as DatrixEntry,\n\t\t\tctx,\n\t\t);\n\t\tconst data = upload\n\t\t\t? await upload.injectUrls(filteredResult)\n\t\t\t: filteredResult;\n\t\treturn jsonResponse({ data });\n\t}\n\n\tconst data = upload ? await upload.injectUrls(result) : result;\n\treturn jsonResponse({ data });\n}\n\n/**\n * Handle DELETE request\n */\nasync function handleDelete(ctx: RequestContext): Promise<Response> {\n\tconst { datrix, schema, id } = ctx;\n\n\tif (!schema) {\n\t\tthrow handlerError.schemaNotFound(ctx.url.pathname);\n\t}\n\n\tif (!id) {\n\t\tthrow handlerError.missingId(\"delete\");\n\t}\n\n\tconst deleted = await datrix.delete(schema.name, id);\n\n\tif (!deleted) {\n\t\tthrow handlerError.recordNotFound(schema.name, id);\n\t}\n\n\treturn jsonResponse({ data: { id, deleted: true } });\n}\n\n/**\n * Unified Request Handler\n *\n * Main entry point - handles all HTTP methods\n *\n * Flow:\n * 1. Build context (auth, parse URL, parse query/body, resolve schema) - ONCE\n * 2. Check schema-level permission (only if auth enabled) - ONCE\n * 3. Route to method handler (which handles field-level permissions if auth enabled)\n */\nexport async function handleCrudRequest<TRole extends string = string>(\n\trequest: Request,\n\tdatrix: Datrix,\n\tapi: IApiPlugin<TRole>,\n\toptions?: ContextBuilderOptions,\n): Promise<Response> {\n\ttry {\n\t\tconst ctx = await buildRequestContext(request, datrix, api, options);\n\n\t\tif (!ctx.schema) {\n\t\t\tthrow handlerError.modelNotSpecified();\n\t\t}\n\n\t\tif (api.excludeSchemas.includes(ctx.schema.name)) {\n\t\t\tthrow handlerError.schemaNotFound(ctx.url.pathname);\n\t\t}\n\n\t\tif (ctx.authEnabled) {\n\t\t\tconst permissionResult = await checkSchemaPermission(\n\t\t\t\tctx.schema,\n\t\t\t\tctx,\n\t\t\t\tapi.authDefaultPermission,\n\t\t\t);\n\n\t\t\tif (!permissionResult.allowed) {\n\t\t\t\tthrow ctx.user\n\t\t\t\t\t? handlerError.permissionDenied(\"Schema scope permission denied\")\n\t\t\t\t\t: handlerError.unauthorized();\n\t\t\t}\n\t\t}\n\n\t\tapi.setUser(ctx.user);\n\n\t\tswitch (ctx.method) {\n\t\t\tcase \"GET\":\n\t\t\t\treturn await handleGet(ctx);\n\t\t\tcase \"POST\":\n\t\t\t\treturn await handlePost(ctx);\n\t\t\tcase \"PATCH\":\n\t\t\tcase \"PUT\":\n\t\t\t\treturn await handleUpdate(ctx);\n\t\t\tcase \"DELETE\":\n\t\t\t\treturn await handleDelete(ctx);\n\t\t\tdefault: {\n\t\t\t\tthrow handlerError.methodNotAllowed(ctx.method);\n\t\t\t}\n\t\t}\n\t} catch (error) {\n\t\tif (error instanceof DatrixValidationError || error instanceof DatrixError) {\n\t\t\treturn datrixErrorResponse(error);\n\t\t}\n\n\t\tconsole.error(\"Unified Handler Error:\", error);\n\t\tconst message =\n\t\t\terror instanceof Error ? error.message : \"Internal server error\";\n\t\treturn datrixErrorResponse(\n\t\t\thandlerError.internalError(\n\t\t\t\tmessage,\n\t\t\t\terror instanceof Error ? error : undefined,\n\t\t\t),\n\t\t);\n\t}\n}\n","/**\n * Datrix API Helper\n *\n * Provides helper functions for handling API requests in user code.\n * Works with any framework that uses the Web Request/Response API natively\n * (Next.js App Router, Hono, Remix, Cloudflare Workers, Bun, Deno).\n * For Node.js frameworks (Express, Fastify, Koa) use toWebRequest / sendWebResponse.\n */\n\nimport { ApiPlugin } from \"../api\";\nimport { Datrix } from \"@datrix/core\";\nimport { datrixErrorResponse } from \"../handler/utils\";\nimport { handlerError } from \"../errors/api-error\";\nimport { DatrixError } from \"@datrix/core\";\n\n// ─── Node.js bridge types ─────────────────────────────────────────────────────\n// Duck-typed — no express/fastify dependency required.\n\ninterface NodeIncomingRequest {\n\tmethod: string;\n\turl?: string;\n\theaders: Record<string, string | string[] | undefined>;\n\tbody?: unknown;\n\tsocket?: { encrypted?: boolean };\n}\n\ninterface NodeOutgoingResponse {\n\tstatusCode: number;\n\tsetHeader(key: string, value: string): unknown;\n\tend(body: string): void;\n}\n\n// ─── Node.js bridge ───────────────────────────────────────────────────────────\n\n/**\n * Convert a Node.js-style incoming request (Express, Fastify, Koa, raw http.IncomingMessage)\n * to a Web API Request.\n *\n * Call this before passing the request to handleRequest.\n *\n * @example\n * ```ts\n * app.all(\"*\", async (req, res) => {\n * const request = toWebRequest(req)\n * const response = await handleRequest(await datrix(), request)\n * await sendWebResponse(res, response)\n * })\n * ```\n */\nexport function toWebRequest(req: NodeIncomingRequest): Request {\n\tconst protocol = req.socket?.encrypted ? \"https\" : \"http\";\n\tconst host = (req.headers[\"host\"] as string | undefined) ?? \"localhost\";\n\tconst url = `${protocol}://${host}${req.url ?? \"/\"}`;\n\n\tconst headers = new Headers();\n\tfor (const [key, value] of Object.entries(req.headers)) {\n\t\tif (value === undefined) continue;\n\t\tif (Array.isArray(value)) {\n\t\t\tfor (const v of value) headers.append(key, v);\n\t\t} else {\n\t\t\theaders.set(key, value);\n\t\t}\n\t}\n\n\tconst hasBody = req.body !== undefined && req.body !== null;\n\tconst methodAllowsBody = ![\"GET\", \"HEAD\"].includes(req.method.toUpperCase());\n\n\tlet body: RequestInit[\"body\"] = undefined!;\n\tif (hasBody && methodAllowsBody) {\n\t\tif (req.body instanceof Uint8Array || Buffer.isBuffer(req.body)) {\n\t\t\tbody = req.body as Uint8Array;\n\t\t} else if (typeof req.body === \"string\") {\n\t\t\tbody = req.body;\n\t\t} else {\n\t\t\tbody = JSON.stringify(req.body);\n\t\t}\n\t}\n\n\treturn new Request(url, {\n\t\tmethod: req.method,\n\t\theaders,\n\t\tbody,\n\t});\n}\n\n/**\n * Write a Web API Response back into a Node.js-style outgoing response\n * (Express, Fastify, Koa, raw http.ServerResponse).\n *\n * @example\n * ```ts\n * app.all(\"*\", async (req, res) => {\n * const request = toWebRequest(req)\n * const response = await handleRequest(await datrix(), request)\n * await sendWebResponse(res, response)\n * })\n * ```\n */\nexport async function sendWebResponse(\n\tres: NodeOutgoingResponse,\n\tresponse: Response,\n): Promise<void> {\n\tres.statusCode = response.status;\n\tresponse.headers.forEach((value, key) => {\n\t\tres.setHeader(key, value);\n\t});\n\tconst body = await response.text();\n\tres.end(body);\n}\n\n/**\n * Handle Datrix API Request\n *\n * This is the ONLY function users need to call in their route handlers.\n *\n * Handles:\n * - Config validation (is api configured?)\n * - API enabled check (is api.enabled = true?)\n * - Error handling (unexpected errors)\n * - Request routing (auth/crud)\n *\n * @param datrix - Datrix instance (must have getConfig() method)\n * @param request - Web API Request\n * @returns Web API Response\n *\n * @example\n * ```ts\n * // Next.js App Router — Web Request/Response native, no bridge needed\n * import datrix from \"@/datrix.config\"\n * import { handleRequest } from \"@datrix/api\"\n *\n * async function handler(request: Request): Promise<Response> {\n * return handleRequest(await datrix(), request)\n * }\n *\n * export const GET = handler\n * export const POST = handler\n * export const PATCH = handler\n * export const PUT = handler\n * export const DELETE = handler\n * ```\n *\n * @example\n * ```ts\n * // Express — use toWebRequest / sendWebResponse bridge\n * import express from \"express\"\n * import datrix from \"./datrix.config\"\n * import { handleRequest, toWebRequest, sendWebResponse } from \"@datrix/api\"\n *\n * const app = express()\n * app.use(express.raw({ type: \"*\\/*\" }))\n *\n * app.all(\"*\", async (req, res) => {\n * const request = toWebRequest(req)\n * const response = await handleRequest(await datrix(), request)\n * await sendWebResponse(res, response)\n * })\n * ```\n */\nexport async function handleRequest(\n\tdatrix: Datrix,\n\trequest: Request,\n): Promise<Response> {\n\ttry {\n\t\t// 1. Check if API is configured\n\t\tconst api = datrix.getPlugin(\"api\");\n\n\t\tif (!api || !(api instanceof ApiPlugin)) {\n\t\t\tconst errRes = handlerError.internalError(\n\t\t\t\t'API is not configured in datrix.config.ts. Add \"api: new DatrixApi({ ... })\" to your configuration.',\n\t\t\t);\n\t\t\treturn datrixErrorResponse(errRes);\n\t\t}\n\n\t\t// 2. Check if API is disabled\n\t\tif (!api.isEnabled()) {\n\t\t\tconst errRes = handlerError.internalError(\n\t\t\t\t'API is disabled. Remove \"disabled: true\" from ApiPlugin configuration.',\n\t\t\t);\n\t\t\treturn datrixErrorResponse(errRes);\n\t\t}\n\n\t\t// 3. Handle request (all logic inside DatrixApi)\n\t\treturn await api.handleRequest(request, datrix);\n\t} catch (error) {\n\t\tif (error instanceof DatrixError) {\n\t\t\treturn datrixErrorResponse(error);\n\t\t}\n\n\t\t// 4. Catch unexpected errors (should rarely happen)\n\t\tconsole.error(\"[Datrix API] Unexpected error:\", error);\n\t\tconst message =\n\t\t\terror instanceof Error ? error.message : \"Internal server error\";\n\t\tconst errRes = handlerError.internalError(\n\t\t\tmessage,\n\t\t\terror instanceof Error ? error : undefined,\n\t\t);\n\t\treturn datrixErrorResponse(errRes);\n\t}\n}\n","/**\n * Auth Middleware\n *\n * Handles authentication from JWT token or session cookie\n */\n\nimport { AuthUser } from \"@datrix/core\";\nimport type { AuthManager } from \"../auth/manager\";\n\n/**\n * Authenticate request\n *\n * Extracts and verifies JWT token or session from request\n * Returns authenticated user or null\n */\nexport async function authenticate(\n\trequest: Request,\n\tauthManager?: AuthManager,\n): Promise<AuthUser | null> {\n\tif (!authManager) {\n\t\treturn null;\n\t}\n\n\t// Use auth manager's authenticate method\n\tconst authContext = await authManager.authenticate(request);\n\n\tif (!authContext || !authContext.user) {\n\t\treturn null;\n\t}\n\n\treturn authContext.user;\n}\n","/**\n * Query Serializer\n *\n * Converts ParsedQuery objects into RawQueryParams query strings.\n */\n\nimport { DatrixEntry, DatrixRecord } from \"@datrix/core\";\nimport { ParsedQuery, RawQueryParams } from \"@datrix/core\";\nimport {\n\tWhereClause,\n\tPopulateClause,\n\tPopulateOptions,\n\tQueryPrimitive,\n} from \"@datrix/core\";\n\nexport function queryToParams<T extends DatrixEntry = DatrixRecord>(\n\tquery: ParsedQuery<T> | undefined,\n): string {\n\tif (!query) return \"\";\n\n\tconst serialized = serializeQuery(query);\n\n\tconst parts: string[] = [];\n\tObject.entries(serialized).forEach(([key, value]) => {\n\t\tif (Array.isArray(value)) {\n\t\t\tvalue.forEach((v) => {\n\t\t\t\t// Only encode the value, keep [ ] in key for readability\n\t\t\t\tparts.push(`${key}=${encodeURIComponent(String(v))}`);\n\t\t\t});\n\t\t} else if (value !== undefined) {\n\t\t\t// Only encode the value, keep [ ] in key for readability\n\t\t\tparts.push(`${key}=${encodeURIComponent(String(value))}`);\n\t\t}\n\t});\n\n\treturn parts.join(\"&\");\n}\n\n/**\n * Serialize ParsedQuery into RawQueryParams\n *\n * @param query - The parsed query object to serialize\n * @returns Records of query parameters\n */\nexport function serializeQuery<T extends DatrixEntry = DatrixEntry>(\n\tquery: ParsedQuery<T>,\n): RawQueryParams {\n\tconst params: Record<string, string | string[]> = {};\n\n\tconst validKeys = [\n\t\t\"select\",\n\t\t\"where\",\n\t\t\"populate\",\n\t\t\"orderBy\",\n\t\t\"page\",\n\t\t\"pageSize\",\n\t];\n\tconst queryKeys = Object.keys(query);\n\tconst unknownKeys = queryKeys.filter((key) => !validKeys.includes(key));\n\n\tif (unknownKeys.length > 0) {\n\t\tthrow new Error(\n\t\t\t`Unknown query keys: ${unknownKeys.join(\", \")}. Valid keys are: ${validKeys.join(\", \")}`,\n\t\t);\n\t}\n\n\t// 1. Fields (select)\n\tif (query.select) {\n\t\tif (query.select === \"*\") {\n\t\t\tparams[\"fields\"] = \"*\";\n\t\t} else if (Array.isArray(query.select)) {\n\t\t\tparams[\"fields\"] = query.select.join(\",\");\n\t\t}\n\t}\n\n\t// 2. Where\n\tif (query.where) {\n\t\tserializeWhere(query.where, \"where\", params);\n\t}\n\n\t// 3. Populate\n\tif (query.populate) {\n\t\tserializePopulate(query.populate, \"populate\", params);\n\t}\n\n\t// 4. OrderBy (sort)\n\tif (query.orderBy) {\n\t\tif (typeof query.orderBy === \"string\") {\n\t\t\t// Simple string format: 'name' or '-name'\n\t\t\tparams[\"sort\"] = query.orderBy;\n\t\t} else if (Array.isArray(query.orderBy)) {\n\t\t\t// Array format: ['name', '-createdAt'] or [{ field: 'name', direction: 'asc' }]\n\t\t\tconst sortStrings = query.orderBy.map((item) => {\n\t\t\t\tif (typeof item === \"string\") {\n\t\t\t\t\t// String item: 'name' or '-name'\n\t\t\t\t\treturn item;\n\t\t\t\t} else {\n\t\t\t\t\t// Object item: { field: 'name', direction: 'asc' }\n\t\t\t\t\treturn item.direction === \"desc\" ? `-${item.field}` : item.field;\n\t\t\t\t}\n\t\t\t});\n\t\t\tif (sortStrings.length > 0) {\n\t\t\t\tparams[\"sort\"] = sortStrings.join(\",\");\n\t\t\t}\n\t\t}\n\t}\n\n\t// 5. Pagination (page/pageSize only)\n\tif (query.page !== undefined) params[\"page\"] = String(query.page);\n\tif (query.pageSize !== undefined) params[\"pageSize\"] = String(query.pageSize);\n\n\treturn params;\n}\n\n/**\n * Recursive helper to serialize where clause\n */\nfunction serializeWhere<T extends DatrixEntry>(\n\twhere: WhereClause<T> | QueryPrimitive | readonly WhereClause<T>[],\n\tprefix: string,\n\tparams: Record<string, string | string[]>,\n) {\n\tif (where === null || typeof where !== \"object\") {\n\t\tparams[prefix] = String(where);\n\t\treturn;\n\t}\n\n\tfor (const [key, value] of Object.entries(where)) {\n\t\tconst newPrefix = `${prefix}[${key}]`;\n\n\t\tif (Array.isArray(value)) {\n\t\t\t// Handle logical operators like $or, $and, $not which take arrays of conditions\n\t\t\tif ([\"$or\", \"$and\", \"$not\"].includes(key)) {\n\t\t\t\tvalue.forEach((item, index) => {\n\t\t\t\t\tserializeWhere(item, `${newPrefix}[${index}]`, params);\n\t\t\t\t});\n\t\t\t} else {\n\t\t\t\t// Handle $in, $nin which take arrays of values\n\t\t\t\tvalue.forEach((item, index) => {\n\t\t\t\t\tparams[`${newPrefix}[${index}]`] = String(item);\n\t\t\t\t});\n\t\t\t}\n\t\t} else if (value !== null && typeof value === \"object\") {\n\t\t\tserializeWhere(value, newPrefix, params);\n\t\t} else {\n\t\t\tparams[newPrefix] = String(value);\n\t\t}\n\t}\n}\n\n/**\n * Recursive helper to serialize populate clause\n */\nfunction serializePopulate<T extends DatrixEntry>(\n\tpopulate: PopulateClause<T> | \"*\",\n\tprefix: string,\n\tparams: Record<string, string | string[] | true>,\n) {\n\tif (populate === \"*\") {\n\t\tparams[prefix] = \"*\";\n\t\treturn;\n\t}\n\n\tif (populate === \"true\") {\n\t\tparams[prefix] = true;\n\t\treturn;\n\t}\n\n\tif (populate === true) {\n\t\tparams[prefix] = true;\n\t\treturn;\n\t}\n\n\t// Handle string[] format: ['relation1', 'relation2']\n\t// Serialize as indexed array (preserves array format)\n\tif (Array.isArray(populate)) {\n\t\tpopulate.forEach((relation: string, index: number) => {\n\t\t\tparams[`${prefix}[${index}]`] = String(relation);\n\t\t});\n\t\treturn;\n\t}\n\n\tif (typeof populate !== \"object\") return;\n\n\tfor (const [relation, options] of Object.entries(populate)) {\n\t\tconst relPrefix = `${prefix}[${relation}]`;\n\n\t\tif (options === \"*\" || options === true) {\n\t\t\tparams[relPrefix] = options;\n\t\t} else if (typeof options === \"object\") {\n\t\t\tconst opts = options as PopulateOptions<T>;\n\n\t\t\t// select fields in populate\n\t\t\tif (opts.select) {\n\t\t\t\tif (opts.select === \"*\") {\n\t\t\t\t\tparams[`${relPrefix}[fields]`] = \"*\";\n\t\t\t\t} else if (Array.isArray(opts.select)) {\n\t\t\t\t\topts.select.forEach((field: string, index: number) => {\n\t\t\t\t\t\tparams[`${relPrefix}[fields][${index}]`] = field;\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// nested populate\n\t\t\tif (opts.populate) {\n\t\t\t\tserializePopulate(opts.populate, `${relPrefix}[populate]`, params);\n\t\t\t}\n\t\t}\n\t}\n}\n"],"mappings":"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","names":["import_core","import_node_crypto","import_core","import_node_crypto","import_core","import_core","import_core","import_core","import_core","import_core","import_core","import_core","import_core","import_core","import_core","MAX_WHERE_VALUE_LENGTH","MAX_LOGICAL_NESTING_DEPTH","import_core","import_core","data","response","import_core"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/api.ts","../src/auth/password.ts","../src/auth/error-helper.ts","../src/auth/jwt.ts","../src/auth/types.ts","../src/auth/session.ts","../src/auth/manager.ts","../src/handler/auth-handler.ts","../src/handler/utils.ts","../src/errors/api-error.ts","../src/errors/auth-error.ts","../src/middleware/permission.ts","../src/parser/query-parser.ts","../src/parser/fields-parser.ts","../src/parser/errors.ts","../src/parser/where-parser.ts","../src/parser/populate-parser.ts","../src/middleware/context.ts","../src/handler/unified.ts","../src/helper/index.ts","../src/middleware/auth.ts","../src/serializer/query.ts"],"sourcesContent":["/**\n * Datrix API Module - Plugin Edition\n *\n * Provides REST API functionality as a Datrix plugin with integrated authentication system.\n *\n * @example\n * ```ts\n * import { createApiPlugin } from '@datrix/api';\n *\n * const apiPlugin = createApiPlugin({\n * enabled: true,\n * prefix: '/api',\n * auth: {\n * enabled: true,\n * jwt: { secret: process.env.JWT_SECRET }\n * }\n * });\n *\n * export default defineConfig(() => ({\n * adapter: new PostgresAdapter({ ... }),\n * schemas: [userSchema],\n * plugins: [apiPlugin]\n * }));\n * ```\n */\n\n// Main exports - Plugin API\nexport { ApiPlugin } from \"./api\";\n\n// Auth exports\nexport { MemorySessionStore } from \"./auth/session\";\n\nexport { handleRequest } from \"./helper\";\n\n// Middleware module (auth, context, permission)\nexport * from \"./middleware\";\n\n// Parser module (query string parsing)\nexport * from \"./parser\";\n\n// Serializer module (response serialization)\nexport * from \"./serializer\";\n\n// Handler module (CRUD and auth handlers)\nexport * from \"./handler\";\n","/**\n * API Plugin\n *\n * Transforms the API package into a Datrix plugin.\n * Manages authentication schema, user sync, and auth routes.\n */\n\nimport { BasePlugin } from \"@datrix/core\";\nimport type {\n\tPluginContext,\n\tQueryContext,\n\tSchemaDefinition,\n} from \"@datrix/core\";\nimport { DefaultPermission, defineSchema } from \"@datrix/core\";\nimport { DEFAULT_API_AUTH_CONFIG } from \"@datrix/core\";\nimport { AuthManager } from \"./auth/manager\";\nimport { createUnifiedAuthHandler } from \"./handler/auth-handler\";\nimport { handleCrudRequest } from \"./handler/unified\";\nimport { handlerError } from \"./errors/api-error\";\nimport { ApiConfig } from \"./types\";\nimport { Datrix } from \"@datrix/core\";\nimport type { IApiPlugin } from \"@datrix/core\";\nimport type { DatrixEntry, DatrixRecord } from \"@datrix/core\";\nimport { datrixErrorResponse } from \"./handler/utils\";\nimport type { AuthUser, IUpload } from \"@datrix/core\";\nimport { QueryObject } from \"@datrix/core\";\nimport { FallbackInput } from \"@datrix/core\";\n\nexport class ApiPlugin<TRole extends string = string>\n\textends BasePlugin<ApiConfig<TRole>>\n\timplements IApiPlugin<TRole>\n{\n\treadonly name = \"api\";\n\treadonly version = \"1.0.0\";\n\n\tpublic authManager?: AuthManager<TRole>;\n\tpublic user: AuthUser | null = null;\n\tprivate datrixInstance?: Datrix;\n\n\tpublic get datrix(): Datrix {\n\t\treturn this.datrixInstance as Datrix;\n\t}\n\n\tpublic get upload(): IUpload | undefined {\n\t\treturn this.options.upload;\n\t}\n\n\tpublic setUser(user: AuthUser | null) {\n\t\tthis.user = user;\n\t}\n\n\tprivate get authConfig(): ApiConfig<TRole>[\"auth\"] | undefined {\n\t\treturn this.options.auth;\n\t}\n\n\tprivate get apiConfig(): ApiConfig<TRole> {\n\t\treturn this.options;\n\t}\n\n\tprivate get authSchemaName(): string {\n\t\treturn this.authConfig?.authSchemaName ?? \"authentication\";\n\t}\n\n\tprivate get userSchemaName(): string {\n\t\treturn this.authConfig?.userSchema?.name ?? \"user\";\n\t}\n\n\tprivate get userSchemaEmailField(): string {\n\t\treturn this.authConfig?.userSchema?.email ?? \"email\";\n\t}\n\n\tpublic get authDefaultPermission(): DefaultPermission<TRole> | undefined {\n\t\treturn this.authConfig?.defaultPermission;\n\t}\n\n\tpublic get authDefaultRole(): TRole | undefined {\n\t\treturn this.authConfig?.defaultRole;\n\t}\n\n\tpublic get excludeSchemas(): readonly string[] {\n\t\treturn [\n\t\t\t...(this.apiConfig.excludeSchemas ?? []),\n\t\t\t\"_datrix\",\n\t\t\t\"_datrix_migrations\",\n\t\t];\n\t}\n\n\tprivate getTableName(schemaName: string): string {\n\t\tconst schema = this.datrix.getSchema(schemaName);\n\t\treturn schema?.tableName || `${schemaName.toLowerCase()}s`;\n\t}\n\n\toverride async onCreateQueryContext(\n\t\tcontext: QueryContext,\n\t): Promise<QueryContext> {\n\t\t// Add authenticated user to context metadata\n\t\tif (this.user) {\n\t\t\tcontext.user = this.user;\n\t\t}\n\n\t\treturn context;\n\t}\n\n\tasync init(context: PluginContext): Promise<void> {\n\t\tthis.context = context;\n\n\t\t// Auth is disabled if authConfig is undefined\n\t\tif (!this.authConfig) {\n\t\t\treturn;\n\t\t}\n\n\t\tif (context.schemas.has(\"auth\")) {\n\t\t\tthrow this.createError(\n\t\t\t\t\"Schema name 'auth' is reserved for API authentication routes\",\n\t\t\t\t\"RESERVED_SCHEMA_NAME\",\n\t\t\t);\n\t\t}\n\n\t\tif (!context.schemas.has(this.userSchemaName)) {\n\t\t\tthrow this.createError(\n\t\t\t\t`User schema '${this.userSchemaName}' not found. Create it before enabling auth.`,\n\t\t\t\t\"USER_SCHEMA_NOT_FOUND\",\n\t\t\t);\n\t\t}\n\n\t\tconst userSchema = context.schemas.get(this.userSchemaName);\n\t\tconst emailField = this.userSchemaEmailField;\n\t\tif (!userSchema?.fields[emailField]) {\n\t\t\tthrow this.createError(\n\t\t\t\t`User schema must have an '${emailField}' field`,\n\t\t\t\t\"MISSING_EMAIL_FIELD\",\n\t\t\t);\n\t\t}\n\n\t\tif (this.authConfig.jwt) {\n\t\t\tif (this.authConfig.jwt.secret.length < 32) {\n\t\t\t\tthrow this.createError(\n\t\t\t\t\t\"JWT secret must be at least 32 characters long for security\",\n\t\t\t\t\t\"WEAK_JWT_SECRET\",\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\tthis.authManager = new AuthManager(this.authConfig);\n\t}\n\n\tasync destroy(): Promise<void> {}\n\n\toverride async getSchemas(): Promise<SchemaDefinition[]> {\n\t\tconst schemas: SchemaDefinition[] = [];\n\n\t\tif (this.options.upload) {\n\t\t\tconst uploadSchemas = await this.options.upload.getSchemas();\n\t\t\tschemas.push(...uploadSchemas);\n\t\t}\n\n\t\tif (!this.authConfig) {\n\t\t\treturn schemas;\n\t\t}\n\n\t\tconst authSchema = defineSchema({\n\t\t\tname: this.authSchemaName,\n\t\t\tfields: {\n\t\t\t\tuser: {\n\t\t\t\t\ttype: \"relation\",\n\t\t\t\t\trequired: true,\n\t\t\t\t\tkind: \"belongsTo\",\n\t\t\t\t\tmodel: this.userSchemaName,\n\t\t\t\t},\n\t\t\t\temail: {\n\t\t\t\t\ttype: \"string\",\n\t\t\t\t\trequired: true,\n\t\t\t\t},\n\t\t\t\tpassword: {\n\t\t\t\t\ttype: \"string\",\n\t\t\t\t\trequired: true,\n\t\t\t\t},\n\t\t\t\tpasswordSalt: {\n\t\t\t\t\ttype: \"string\",\n\t\t\t\t\trequired: true,\n\t\t\t\t},\n\t\t\t\trole: {\n\t\t\t\t\ttype: \"string\",\n\t\t\t\t\trequired: true,\n\t\t\t\t\tdefault: this.authDefaultRole ?? \"user\",\n\t\t\t\t},\n\t\t\t},\n\t\t\tindexes: [\n\t\t\t\t{\n\t\t\t\t\tname: `${this.authSchemaName}_email_idx`,\n\t\t\t\t\tfields: [\"email\"],\n\t\t\t\t\tunique: true,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tname: `${this.authSchemaName}_userId_idx`,\n\t\t\t\t\tfields: [\"user\"],\n\t\t\t\t\tunique: true,\n\t\t\t\t},\n\t\t\t],\n\t\t});\n\n\t\tschemas.push(authSchema);\n\t\treturn schemas;\n\t}\n\n\toverride async onBeforeQuery<T extends DatrixEntry>(\n\t\tquery: QueryObject<T>,\n\t\tcontext: QueryContext,\n\t): Promise<QueryObject<T>> {\n\t\tif (!this.authConfig) {\n\t\t\treturn query;\n\t\t}\n\n\t\tconst userTable = this.getTableName(this.userSchemaName);\n\n\t\t// User insert → store flag in metadata\n\t\tif (query.type === \"insert\" && query.table === userTable) {\n\t\t\tcontext.metadata[\"api:createAuth\"] = true;\n\t\t\tcontext.metadata[\"api:userData\"] = query.data[0];\n\t\t}\n\n\t\t// User email update → store flag in metadata\n\t\tif (query.type === \"update\" && query.table === userTable) {\n\t\t\tconst data = query.data;\n\t\t\tconst emailField = this.userSchemaEmailField;\n\t\t\tif (data && emailField in data) {\n\t\t\t\tcontext.metadata[\"api:syncEmail\"] = (\n\t\t\t\t\tquery.data as Record<string, unknown>\n\t\t\t\t)[emailField];\n\t\t\t\tcontext.metadata[\"api:userId\"] = query.where?.[\"id\"];\n\t\t\t}\n\t\t}\n\n\t\treturn query;\n\t}\n\n\toverride async onAfterQuery<TResult>(\n\t\tresult: TResult,\n\t\tcontext: QueryContext,\n\t): Promise<TResult> {\n\t\tif (!this.authConfig) {\n\t\t\treturn result;\n\t\t}\n\n\t\tconst pluginContext = this.getContext();\n\n\t\t// User created → create authentication record\n\t\tif (context.metadata[\"api:createAuth\"]) {\n\t\t\tconst { id: userId } = Array.isArray(result) ? result[0] : result;\n\t\t\tif (typeof userId === \"number\") {\n\t\t\t\tconst user: Partial<DatrixRecord> = {\n\t\t\t\t\t...(context.metadata[\"api:userData\"] as Record<string, unknown>),\n\t\t\t\t\tuserId,\n\t\t\t\t};\n\t\t\t\tawait this.createAuthenticationRecord(user, pluginContext);\n\t\t\t}\n\t\t}\n\n\t\t// User email updated → sync authentication email\n\t\tif (context.metadata[\"api:syncEmail\"] && context.metadata[\"api:userId\"]) {\n\t\t\tconst newEmail = context.metadata[\"api:syncEmail\"] as string;\n\t\t\tconst userId = context.metadata[\"api:userId\"] as string;\n\t\t\tawait this.syncAuthenticationEmail(userId, newEmail, pluginContext);\n\t\t}\n\n\t\treturn result;\n\t}\n\n\tprivate async createAuthenticationRecord(\n\t\t_user: Partial<DatrixRecord>,\n\t\t_context: PluginContext,\n\t): Promise<void> {\n\t\tconst emailField = this.userSchemaEmailField;\n\t\tconst user = _user as FallbackInput;\n\t\tconst authData: FallbackInput = {\n\t\t\tuser: user[\"userId\"]!,\n\t\t\temail: user[emailField]!,\n\t\t\tpassword: user[\"password\"] || \"\",\n\t\t\tpasswordSalt: user[\"passwordSalt\"] || \"\",\n\t\t\trole: user[\"role\"] || this.authConfig?.defaultRole || \"user\",\n\t\t};\n\n\t\tawait this.datrixInstance!.raw.create(this.authSchemaName, authData);\n\t}\n\n\tprivate async syncAuthenticationEmail(\n\t\tuserId: string,\n\t\tnewEmail: string,\n\t\t_context: PluginContext,\n\t): Promise<void> {\n\t\tawait this.datrix.raw.updateMany(\n\t\t\tthis.authSchemaName,\n\t\t\t{ user: { id: { $eq: userId } } },\n\t\t\t{ email: newEmail },\n\t\t);\n\t}\n\n\t/**\n\t * Handle HTTP request\n\t *\n\t * Main entry point for all API requests.\n\t * Routes to auth handlers or CRUD handlers.\n\t */\n\tasync handleRequest(request: Request, datrix: Datrix): Promise<Response> {\n\t\tif (!this.isInitialized()) {\n\t\t\treturn datrixErrorResponse(\n\t\t\t\thandlerError.internalError(\"API plugin not initialized\"),\n\t\t\t);\n\t\t}\n\n\t\tthis.datrixInstance = datrix;\n\n\t\tconst url = new URL(request.url);\n\t\tconst prefix = this.apiConfig.prefix ?? \"/api\";\n\n\t\tif (!url.pathname.startsWith(prefix)) {\n\t\t\treturn datrixErrorResponse(\n\t\t\t\thandlerError.internalError(\"Invalid API prefix\"),\n\t\t\t);\n\t\t}\n\n\t\tconst pathAfterPrefix = url.pathname.slice(prefix.length);\n\t\tconst segments = pathAfterPrefix.split(\"/\").filter(Boolean);\n\t\tconst model = segments[0];\n\n\t\tif (this.authConfig && this.isAuthPath(pathAfterPrefix)) {\n\t\t\treturn this.handleAuthRequest(request, datrix);\n\t\t}\n\n\t\tif (\n\t\t\tmodel === \"upload\" &&\n\t\t\tthis.apiConfig.upload &&\n\t\t\trequest.method !== \"GET\"\n\t\t) {\n\t\t\treturn this.apiConfig.upload.handleRequest(request, datrix);\n\t\t}\n\n\t\treturn handleCrudRequest(request, datrix, this, {\n\t\t\tapiPrefix: prefix,\n\t\t});\n\t}\n\n\tprivate isAuthPath(pathname: string): boolean {\n\t\tconst e = this.authConfig?.endpoints;\n\t\tconst d = DEFAULT_API_AUTH_CONFIG.endpoints;\n\t\tconst login = e?.login ?? d.login;\n\t\tconst register = e?.register ?? d.register;\n\t\tconst logout = e?.logout ?? d.logout;\n\t\tconst me = e?.me ?? d.me;\n\n\t\tconst authPrefix =\n\t\t\t[login, register, logout, me].map((p) => p.split(\"/\")[1]).find(Boolean) ??\n\t\t\t\"auth\";\n\n\t\treturn (\n\t\t\tpathname.startsWith(`/${authPrefix}/`) || pathname === `/${authPrefix}`\n\t\t);\n\t}\n\n\t/**\n\t * Handle authentication requests\n\t */\n\tprivate async handleAuthRequest(\n\t\trequest: Request,\n\t\tdatrix: Datrix,\n\t): Promise<Response> {\n\t\tif (!this.authManager) {\n\t\t\treturn datrixErrorResponse(\n\t\t\t\thandlerError.internalError(\"Authentication not configured\"),\n\t\t\t);\n\t\t}\n\n\t\tconst handler = createUnifiedAuthHandler(\n\t\t\t{\n\t\t\t\tdatrix,\n\t\t\t\tauthManager: this.authManager,\n\t\t\t\tauthConfig: this.authConfig!,\n\t\t\t},\n\t\t\tthis.apiConfig.prefix ?? \"/api\",\n\t\t);\n\n\t\treturn handler(request);\n\t}\n\n\t/**\n\t * Check if API is enabled\n\t */\n\tisEnabled(): boolean {\n\t\treturn !(this.apiConfig.disabled ?? false);\n\t}\n\n\t/**\n\t * Check if authentication is enabled\n\t */\n\tisAuthEnabled(): boolean {\n\t\treturn this.authConfig !== undefined;\n\t}\n\n\t/**\n\t * Get auth manager (for external use)\n\t */\n\tgetAuthManager(): AuthManager<TRole> | undefined {\n\t\treturn this.authManager;\n\t}\n}\n","/**\n * Password Utilities\n *\n * Handles password hashing and verification using PBKDF2\n */\n\nimport { pbkdf2Sync, randomBytes, timingSafeEqual } from \"node:crypto\";\nimport {\n\tthrowPasswordTooShort,\n\tthrowPasswordHashError,\n\tthrowPasswordVerifyError,\n} from \"./error-helper\";\n\n/**\n * Password hash result\n */\nexport interface PasswordHash {\n\treadonly hash: string;\n\treadonly salt: string;\n}\n\n/**\n * Password configuration\n */\nexport interface PasswordConfig {\n\treadonly iterations?: number; // PBKDF2 iterations (default: 100000)\n\treadonly keyLength?: number; // PBKDF2 key length (default: 64)\n\treadonly minLength?: number; // Minimum password length (default: 8)\n}\n\n/**\n * Default password configuration\n */\nconst DEFAULT_CONFIG: Required<PasswordConfig> = {\n\titerations: 100000,\n\tkeyLength: 64,\n\tminLength: 8,\n};\n\n/**\n * Password Manager\n *\n * Manages password hashing and verification\n */\nexport class PasswordManager {\n\tprivate readonly iterations: number;\n\tprivate readonly keyLength: number;\n\tprivate readonly minLength: number;\n\n\tconstructor(config: PasswordConfig = {}) {\n\t\tthis.iterations = config.iterations ?? DEFAULT_CONFIG.iterations;\n\t\tthis.keyLength = config.keyLength ?? DEFAULT_CONFIG.keyLength;\n\t\tthis.minLength = config.minLength ?? DEFAULT_CONFIG.minLength;\n\t}\n\n\t/**\n\t * Hash password using PBKDF2\n\t */\n\tasync hash(password: string): Promise<PasswordHash> {\n\t\t// Validate password strength\n\t\tif (!password || password.length < this.minLength) {\n\t\t\tthrowPasswordTooShort(this.minLength, password?.length ?? 0);\n\t\t}\n\n\t\ttry {\n\t\t\tconst salt = randomBytes(32).toString(\"hex\");\n\n\t\t\tconst hash = pbkdf2Sync(\n\t\t\t\tpassword,\n\t\t\t\tsalt,\n\t\t\t\tthis.iterations,\n\t\t\t\tthis.keyLength,\n\t\t\t\t\"sha512\",\n\t\t\t).toString(\"hex\");\n\n\t\t\treturn { hash, salt };\n\t\t} catch (error) {\n\t\t\tthrowPasswordHashError(error instanceof Error ? error : undefined);\n\t\t}\n\t}\n\n\t/**\n\t * Verify password against hash\n\t */\n\tasync verify(password: string, hash: string, salt: string): Promise<boolean> {\n\t\ttry {\n\t\t\tconst computedHash = pbkdf2Sync(\n\t\t\t\tpassword,\n\t\t\t\tsalt,\n\t\t\t\tthis.iterations,\n\t\t\t\tthis.keyLength,\n\t\t\t\t\"sha512\",\n\t\t\t).toString(\"hex\");\n\n\t\t\t// Constant-time comparison to prevent timing attacks\n\t\t\tconst isValid = this.constantTimeCompare(computedHash, hash);\n\n\t\t\treturn isValid;\n\t\t} catch (error) {\n\t\t\tthrowPasswordVerifyError(error instanceof Error ? error : undefined);\n\t\t}\n\t}\n\n\t/**\n\t * Constant-time string comparison (prevent timing attacks)\n\t */\n\tprivate constantTimeCompare(a: string, b: string): boolean {\n\t\tif (a.length !== b.length) {\n\t\t\treturn false;\n\t\t}\n\n\t\ttry {\n\t\t\tconst bufA = Buffer.from(a);\n\t\t\tconst bufB = Buffer.from(b);\n\t\t\treturn timingSafeEqual(bufA, bufB);\n\t\t} catch {\n\t\t\t// Fallback to manual constant-time comparison\n\t\t\tlet result = 0;\n\t\t\tfor (let i = 0; i < a.length; i++) {\n\t\t\t\tresult |= (a.charCodeAt(i) as number) ^ (b.charCodeAt(i) as number);\n\t\t\t}\n\t\t\treturn result === 0;\n\t\t}\n\t}\n}\n\n/**\n * Create password manager\n */\nexport function createPasswordManager(\n\tconfig?: PasswordConfig,\n): PasswordManager {\n\treturn new PasswordManager(config);\n}\n","/**\n * Auth Error Helper Functions\n *\n * Centralized error throwing functions for authentication module.\n * Provides consistent error messages and suggestions.\n */\n\nimport { DatrixAuthError } from \"@datrix/core\";\n\n// ============================================================================\n// JWT Errors\n// ============================================================================\n\n/**\n * Throw JWT sign error\n *\n * @param cause - Original error\n */\nexport function throwJwtSignError(cause?: Error): never {\n\tthrow new DatrixAuthError(\"Failed to sign JWT token\", {\n\t\tcode: \"JWT_SIGN_ERROR\",\n\t\tstrategy: \"jwt\",\n\t\tcause,\n\t\tsuggestion: \"Check your JWT configuration and secret key\",\n\t});\n}\n\n/**\n * Throw JWT verify error\n *\n * @param cause - Original error\n */\nexport function throwJwtVerifyError(cause?: Error): never {\n\tthrow new DatrixAuthError(\"Failed to verify JWT token\", {\n\t\tcode: \"JWT_VERIFY_ERROR\",\n\t\tstrategy: \"jwt\",\n\t\tcause,\n\t\tsuggestion: \"Ensure the token is valid and not tampered with\",\n\t});\n}\n\n/**\n * Throw JWT decode error\n *\n * @param cause - Original error\n */\nexport function throwJwtDecodeError(cause?: Error): never {\n\tthrow new DatrixAuthError(\"Failed to decode JWT token\", {\n\t\tcode: \"JWT_DECODE_ERROR\",\n\t\tstrategy: \"jwt\",\n\t\tcause,\n\t\tsuggestion: \"Ensure the token format is correct\",\n\t});\n}\n\n/**\n * Throw JWT invalid format error\n */\nexport function throwJwtInvalidFormat(): never {\n\tthrow new DatrixAuthError(\"Invalid JWT format\", {\n\t\tcode: \"JWT_INVALID_FORMAT\",\n\t\tstrategy: \"jwt\",\n\t\tsuggestion: \"JWT must be in format: header.payload.signature\",\n\t\texpected: \"three dot-separated base64url strings\",\n\t});\n}\n\n/**\n * Throw JWT invalid header error\n */\nexport function throwJwtInvalidHeader(): never {\n\tthrow new DatrixAuthError(\"Invalid JWT header\", {\n\t\tcode: \"JWT_INVALID_HEADER\",\n\t\tstrategy: \"jwt\",\n\t\tsuggestion: \"Ensure the JWT header has correct algorithm and type\",\n\t\texpected: 'header with typ: \"JWT\" and matching algorithm',\n\t});\n}\n\n/**\n * Throw JWT invalid payload error\n */\nexport function throwJwtInvalidPayload(): never {\n\tthrow new DatrixAuthError(\"Invalid JWT payload\", {\n\t\tcode: \"JWT_INVALID_PAYLOAD\",\n\t\tstrategy: \"jwt\",\n\t\tsuggestion: \"JWT payload must contain userId, role, iat, and exp fields\",\n\t\texpected: \"valid JWT payload with required fields\",\n\t});\n}\n\n/**\n * Throw JWT invalid signature error\n */\nexport function throwJwtInvalidSignature(): never {\n\tthrow new DatrixAuthError(\"Invalid JWT signature\", {\n\t\tcode: \"JWT_INVALID_SIGNATURE\",\n\t\tstrategy: \"jwt\",\n\t\tsuggestion: \"Token may have been tampered with or signed with wrong secret\",\n\t\texpected: \"valid HMAC signature\",\n\t});\n}\n\n/**\n * Throw JWT expired error\n *\n * @param exp - Token expiration timestamp\n * @param now - Current timestamp\n */\nexport function throwJwtExpired(exp: number, now: number): never {\n\tthrow new DatrixAuthError(\"JWT token expired\", {\n\t\tcode: \"JWT_EXPIRED\",\n\t\tstrategy: \"jwt\",\n\t\tcontext: { exp, now },\n\t\tsuggestion: \"Refresh your token or login again\",\n\t\texpected: \"token with exp > current time\",\n\t});\n}\n\n/**\n * Throw JWT invalid issued at time error\n */\nexport function throwJwtInvalidIat(): never {\n\tthrow new DatrixAuthError(\"JWT token issued in the future\", {\n\t\tcode: \"JWT_INVALID_IAT\",\n\t\tstrategy: \"jwt\",\n\t\tsuggestion: \"Check your server time synchronization\",\n\t\texpected: \"token with iat <= current time (allowing 60s skew)\",\n\t});\n}\n\n/**\n * Throw JWT invalid issuer error\n *\n * @param expected - Expected issuer\n * @param received - Received issuer\n */\nexport function throwJwtInvalidIssuer(\n\texpected: string,\n\treceived: string | undefined,\n): never {\n\tthrow new DatrixAuthError(\"JWT issuer mismatch\", {\n\t\tcode: \"JWT_INVALID_ISSUER\",\n\t\tstrategy: \"jwt\",\n\t\texpected,\n\t\treceived,\n\t\tsuggestion: `Token must be issued by: ${expected}`,\n\t});\n}\n\n/**\n * Throw JWT invalid audience error\n *\n * @param expected - Expected audience\n * @param received - Received audience\n */\nexport function throwJwtInvalidAudience(\n\texpected: string,\n\treceived: string | undefined,\n): never {\n\tthrow new DatrixAuthError(\"JWT audience mismatch\", {\n\t\tcode: \"JWT_INVALID_AUDIENCE\",\n\t\tstrategy: \"jwt\",\n\t\texpected,\n\t\treceived,\n\t\tsuggestion: `Token must be for audience: ${expected}`,\n\t});\n}\n\n// ============================================================================\n// Session Errors\n// ============================================================================\n\n/**\n * Throw session create error\n *\n * @param cause - Original error\n */\nexport function throwSessionCreateError(cause?: Error): never {\n\tthrow new DatrixAuthError(\"Failed to create session\", {\n\t\tcode: \"SESSION_CREATE_ERROR\",\n\t\tstrategy: \"session\",\n\t\tcause,\n\t\tsuggestion: \"Check your session store configuration\",\n\t});\n}\n\n/**\n * Throw session not found error\n *\n * @param sessionId - Session ID\n */\nexport function throwSessionNotFound(sessionId?: string): never {\n\tthrow new DatrixAuthError(\"Session not found\", {\n\t\tcode: \"AUTH_SESSION_NOT_FOUND\",\n\t\tstrategy: \"session\",\n\t\tcontext: { sessionId },\n\t\tsuggestion: \"Login again to create a new session\",\n\t});\n}\n\n/**\n * Throw session expired error\n *\n * @param sessionId - Session ID\n */\nexport function throwSessionExpired(sessionId?: string): never {\n\tthrow new DatrixAuthError(\"Session expired\", {\n\t\tcode: \"AUTH_SESSION_EXPIRED\",\n\t\tstrategy: \"session\",\n\t\tcontext: { sessionId },\n\t\tsuggestion: \"Login again to create a new session\",\n\t});\n}\n\n/**\n * Throw session delete error\n *\n * @param cause - Original error\n */\nexport function throwSessionDeleteError(cause?: Error): never {\n\tthrow new DatrixAuthError(\"Failed to delete session\", {\n\t\tcode: \"SESSION_DELETE_ERROR\",\n\t\tstrategy: \"session\",\n\t\tcause,\n\t\tsuggestion: \"Check your session store configuration\",\n\t});\n}\n\n/**\n * Throw session not configured error\n */\nexport function throwSessionNotConfigured(): never {\n\tthrow new DatrixAuthError(\"Session strategy not configured\", {\n\t\tcode: \"SESSION_NOT_CONFIGURED\",\n\t\tstrategy: \"session\",\n\t\tsuggestion: \"Add session configuration to your auth config\",\n\t\texpected: \"AuthConfig.session to be defined\",\n\t});\n}\n\n// ============================================================================\n// Password Errors\n// ============================================================================\n\n/**\n * Throw password too short error\n *\n * @param minLength - Minimum password length\n * @param actualLength - Actual password length\n */\nexport function throwPasswordTooShort(\n\tminLength: number,\n\tactualLength: number,\n): never {\n\tthrow new DatrixAuthError(\n\t\t`Password must be at least ${minLength} characters`,\n\t\t{\n\t\t\tcode: \"PASSWORD_TOO_SHORT\",\n\t\t\tstrategy: \"password\",\n\t\t\tcontext: { minLength, actualLength },\n\t\t\tsuggestion: `Use a password with at least ${minLength} characters`,\n\t\t\texpected: `password length >= ${minLength}`,\n\t\t\treceived: actualLength,\n\t\t},\n\t);\n}\n\n/**\n * Throw password hash error\n *\n * @param cause - Original error\n */\nexport function throwPasswordHashError(cause?: Error): never {\n\tthrow new DatrixAuthError(\"Failed to hash password\", {\n\t\tcode: \"PASSWORD_HASH_ERROR\",\n\t\tstrategy: \"password\",\n\t\tcause,\n\t\tsuggestion: \"Check your password hashing configuration\",\n\t});\n}\n\n/**\n * Throw password verify error\n *\n * @param cause - Original error\n */\nexport function throwPasswordVerifyError(cause?: Error): never {\n\tthrow new DatrixAuthError(\"Failed to verify password\", {\n\t\tcode: \"PASSWORD_VERIFY_ERROR\",\n\t\tstrategy: \"password\",\n\t\tcause,\n\t\tsuggestion: \"Ensure the password hash and salt are valid\",\n\t});\n}\n\n// ============================================================================\n// General Auth Errors\n// ============================================================================\n\n/**\n * Throw invalid credentials error\n */\nexport function throwInvalidCredentials(): never {\n\tthrow new DatrixAuthError(\"Invalid email or password\", {\n\t\tcode: \"AUTH_INVALID_CREDENTIALS\",\n\t\tsuggestion: \"Check your email and password and try again\",\n\t});\n}\n\n/**\n * Throw user not found error\n *\n * @param email - User email\n */\nexport function throwUserNotFound(email?: string): never {\n\tthrow new DatrixAuthError(\"User not found\", {\n\t\tcode: \"AUTH_USER_NOT_FOUND\",\n\t\tcontext: { email },\n\t\tsuggestion: email\n\t\t\t? `No user found with email: ${email}`\n\t\t\t: \"User does not exist\",\n\t});\n}\n\n/**\n * Throw user already exists error\n *\n * @param email - User email\n */\nexport function throwUserExists(email: string): never {\n\tthrow new DatrixAuthError(`User with email ${email} already exists`, {\n\t\tcode: \"AUTH_USER_EXISTS\",\n\t\tcontext: { email },\n\t\tsuggestion: \"Use a different email or try logging in\",\n\t});\n}\n\n/**\n * Throw unauthorized error\n *\n * @param message - Custom message\n */\nexport function throwUnauthorized(message = \"Authentication required\"): never {\n\tthrow new DatrixAuthError(message, {\n\t\tcode: \"AUTH_UNAUTHORIZED\",\n\t\tsuggestion: \"Provide valid authentication credentials\",\n\t});\n}\n\n/**\n * Throw forbidden error\n *\n * @param action - Action attempted\n * @param resource - Resource name\n * @param role - User role\n */\nexport function throwForbidden(\n\taction?: string,\n\tresource?: string,\n\trole?: string,\n): never {\n\tconst message =\n\t\taction && resource\n\t\t\t? `You don't have permission to ${action} ${resource}`\n\t\t\t: \"Access forbidden\";\n\n\tthrow new DatrixAuthError(message, {\n\t\tcode: \"AUTH_FORBIDDEN\",\n\t\tstrategy: \"permission\",\n\t\tcontext: { action, resource, role },\n\t\tsuggestion: \"Contact your administrator for access\",\n\t});\n}\n\n/**\n * Throw permission denied error\n *\n * @param action - Action attempted\n * @param resource - Resource name\n * @param field - Field name (optional)\n */\nexport function throwPermissionDenied(\n\taction: string,\n\tresource: string,\n\tfield?: string,\n): never {\n\tconst message = field\n\t\t? `Permission denied: Cannot ${action} field \"${field}\" on ${resource}`\n\t\t: `Permission denied: Cannot ${action} ${resource}`;\n\n\tthrow new DatrixAuthError(message, {\n\t\tcode: \"PERMISSION_DENIED\",\n\t\tstrategy: \"permission\",\n\t\tcontext: { action, resource, field },\n\t\tsuggestion: \"Check your role permissions\",\n\t});\n}\n\n/**\n * Throw auth config invalid error\n *\n * @param message - Error message\n * @param field - Config field name\n */\nexport function throwAuthConfigInvalid(message: string, field?: string): never {\n\tthrow new DatrixAuthError(message, {\n\t\tcode: \"AUTH_CONFIG_INVALID\",\n\t\tcontext: { field },\n\t\tsuggestion: \"Check your authentication configuration\",\n\t});\n}\n","/**\n * JWT Strategy\n *\n * Implements JWT token signing and verification using Node.js crypto module.\n * No external dependencies (no jsonwebtoken library).\n */\n\nimport { createHmac, timingSafeEqual } from \"node:crypto\";\nimport type {\n\tJwtConfig,\n\tJwtPayload,\n\tJwtHeader,\n\tJwtAlgorithm,\n\tTimeUnit,\n\tExpiryString,\n} from \"./types\";\nimport { DEFAULT_API_AUTH_CONFIG } from \"@datrix/core\";\nimport { isJwtPayload } from \"./types\";\nimport {\n\tthrowJwtSignError,\n\tthrowJwtVerifyError,\n\tthrowJwtDecodeError,\n\tthrowJwtInvalidFormat,\n\tthrowJwtInvalidHeader,\n\tthrowJwtInvalidPayload,\n\tthrowJwtInvalidSignature,\n\tthrowJwtExpired,\n\tthrowJwtInvalidIat,\n\tthrowJwtInvalidIssuer,\n\tthrowJwtInvalidAudience,\n} from \"./error-helper\";\n\n/**\n * JWT Strategy\n *\n * Handles JWT token creation and verification\n */\nexport class JwtStrategy {\n\tprivate readonly secret: string;\n\tprivate readonly expiresIn: number; // in seconds\n\tprivate readonly algorithm: JwtAlgorithm;\n\tprivate readonly issuer: string | undefined;\n\tprivate readonly audience: string | undefined;\n\n\tconstructor(config: JwtConfig) {\n\t\tthis.secret = config.secret;\n\t\tthis.expiresIn = this.parseExpiry(\n\t\t\tconfig.expiresIn ?? DEFAULT_API_AUTH_CONFIG.jwt.expiresIn,\n\t\t);\n\t\tthis.algorithm = config.algorithm ?? \"HS256\";\n\t\tthis.issuer = config.issuer;\n\t\tthis.audience = config.audience;\n\t}\n\n\t/**\n\t * Sign a JWT token\n\t */\n\tsign(payload: Omit<JwtPayload, \"iat\" | \"exp\" | \"iss\" | \"aud\">): string {\n\t\ttry {\n\t\t\tconst now = Math.floor(Date.now() / 1000);\n\t\t\tconst exp = now + this.expiresIn;\n\n\t\t\tconst basePayload = payload as Record<string, unknown>;\n\t\t\tconst fullPayload: JwtPayload = {\n\t\t\t\tuserId: basePayload[\"userId\"] as number,\n\t\t\t\trole: basePayload[\"role\"] as string,\n\t\t\t\tiat: now,\n\t\t\t\texp,\n\t\t\t\t...(this.issuer && { iss: this.issuer }),\n\t\t\t\t...(this.audience && { aud: this.audience }),\n\t\t\t\t...basePayload,\n\t\t\t};\n\n\t\t\tconst token = this.createToken(fullPayload);\n\n\t\t\treturn token;\n\t\t} catch (error) {\n\t\t\tthrowJwtSignError(error instanceof Error ? error : undefined);\n\t\t}\n\t}\n\n\t/**\n\t * Verify a JWT token\n\t */\n\tverify(token: string): JwtPayload {\n\t\ttry {\n\t\t\t// Split token into parts\n\t\t\tconst parts = token.split(\".\");\n\t\t\tif (parts.length !== 3) {\n\t\t\t\tthrowJwtInvalidFormat();\n\t\t\t}\n\n\t\t\tconst encodedHeader = parts[0];\n\t\t\tconst encodedPayload = parts[1];\n\t\t\tconst signature = parts[2];\n\n\t\t\tif (!encodedHeader || !encodedPayload || !signature) {\n\t\t\t\tthrowJwtInvalidFormat();\n\t\t\t}\n\n\t\t\t// Verify signature\n\t\t\tconst expectedSignature = this.signData(\n\t\t\t\t`${encodedHeader}.${encodedPayload}`,\n\t\t\t);\n\n\t\t\tif (!this.constantTimeCompare(signature, expectedSignature)) {\n\t\t\t\tthrowJwtInvalidSignature();\n\t\t\t}\n\n\t\t\t// Decode and validate header\n\t\t\tconst header = this.decodeBase64Url<JwtHeader>(encodedHeader);\n\t\t\tif (!header || header.typ !== \"JWT\" || header.alg !== this.algorithm) {\n\t\t\t\tthrowJwtInvalidHeader();\n\t\t\t}\n\n\t\t\t// Decode and validate payload\n\t\t\tconst payload = this.decodeBase64Url<JwtPayload>(encodedPayload);\n\t\t\tif (!payload || !isJwtPayload(payload)) {\n\t\t\t\tthrowJwtInvalidPayload();\n\t\t\t}\n\n\t\t\t// Check expiration\n\t\t\tconst now = Math.floor(Date.now() / 1000);\n\t\t\tif (payload.exp < now) {\n\t\t\t\tthrowJwtExpired(payload.exp, now);\n\t\t\t}\n\n\t\t\t// Check issued at (not in future)\n\t\t\tif (payload.iat > now + 60) {\n\t\t\t\t// Allow 60s clock skew\n\t\t\t\tthrowJwtInvalidIat();\n\t\t\t}\n\n\t\t\t// Check issuer if configured\n\t\t\tif (this.issuer !== undefined && payload.iss !== this.issuer) {\n\t\t\t\tthrowJwtInvalidIssuer(this.issuer, payload.iss);\n\t\t\t}\n\n\t\t\t// Check audience if configured\n\t\t\tif (this.audience !== undefined && payload.aud !== this.audience) {\n\t\t\t\tthrowJwtInvalidAudience(this.audience, payload.aud);\n\t\t\t}\n\n\t\t\treturn payload;\n\t\t} catch (error) {\n\t\t\tif (error instanceof Error && error.name === \"DatrixAuthError\") {\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t\tthrowJwtVerifyError(error instanceof Error ? error : undefined);\n\t\t}\n\t}\n\n\t/**\n\t * Refresh a JWT token\n\t *\n\t * Creates a new token with updated expiration\n\t */\n\trefresh(token: string): string {\n\t\tconst payload = this.verify(token);\n\n\t\tconst { userId, role, ...rest } = payload;\n\n\t\t// Remove standard claims\n\t\tconst { iat: _iat, exp: _exp, iss: _iss, aud: _aud, ...custom } = rest;\n\n\t\treturn this.sign({ userId, role, ...custom });\n\t}\n\n\t/**\n\t * Decode token without verification (for debugging)\n\t */\n\tdecode(token: string): JwtPayload {\n\t\ttry {\n\t\t\tconst parts = token.split(\".\");\n\t\t\tif (parts.length !== 3) {\n\t\t\t\tthrowJwtInvalidFormat();\n\t\t\t}\n\n\t\t\tconst encodedPayload = parts[1];\n\t\t\tif (!encodedPayload) {\n\t\t\t\tthrowJwtInvalidFormat();\n\t\t\t}\n\n\t\t\tconst payload = this.decodeBase64Url<JwtPayload>(encodedPayload);\n\t\t\tif (!payload || !isJwtPayload(payload)) {\n\t\t\t\tthrowJwtInvalidPayload();\n\t\t\t}\n\n\t\t\treturn payload;\n\t\t} catch (error) {\n\t\t\tif (error instanceof Error && error.name === \"DatrixAuthError\") {\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t\tthrowJwtDecodeError(error instanceof Error ? error : undefined);\n\t\t}\n\t}\n\n\t/**\n\t * Create a JWT token from payload\n\t */\n\tprivate createToken(payload: JwtPayload): string {\n\t\tconst header: JwtHeader = {\n\t\t\talg: this.algorithm,\n\t\t\ttyp: \"JWT\",\n\t\t};\n\n\t\tconst encodedHeader = this.encodeBase64Url(JSON.stringify(header));\n\t\tconst encodedPayload = this.encodeBase64Url(JSON.stringify(payload));\n\n\t\tconst signature = this.signData(`${encodedHeader}.${encodedPayload}`);\n\n\t\treturn `${encodedHeader}.${encodedPayload}.${signature}`;\n\t}\n\n\t/**\n\t * Sign data using HMAC\n\t */\n\tprivate signData(data: string): string {\n\t\tconst algorithm = this.algorithm === \"HS256\" ? \"sha256\" : \"sha512\";\n\t\tconst hmac = createHmac(algorithm, this.secret);\n\t\thmac.update(data);\n\t\treturn this.encodeBase64Url(hmac.digest(\"base64\"));\n\t}\n\n\t/**\n\t * Base64 URL encode\n\t */\n\tprivate encodeBase64Url(str: string): string {\n\t\treturn Buffer.from(str)\n\t\t\t.toString(\"base64\")\n\t\t\t.replace(/\\+/g, \"-\")\n\t\t\t.replace(/\\//g, \"_\")\n\t\t\t.replace(/=/g, \"\");\n\t}\n\n\t/**\n\t * Base64 URL decode\n\t */\n\tprivate decodeBase64Url<T>(str: string): T | undefined {\n\t\ttry {\n\t\t\t// Add padding if needed\n\t\t\tlet padded = str;\n\t\t\twhile (padded.length % 4 !== 0) {\n\t\t\t\tpadded += \"=\";\n\t\t\t}\n\n\t\t\t// Replace URL-safe characters\n\t\t\tconst base64 = padded.replace(/-/g, \"+\").replace(/_/g, \"/\");\n\n\t\t\tconst decoded = Buffer.from(base64, \"base64\").toString(\"utf8\");\n\t\t\treturn JSON.parse(decoded) as T;\n\t\t} catch {\n\t\t\treturn undefined;\n\t\t}\n\t}\n\n\t/**\n\t * Constant-time string comparison (prevent timing attacks)\n\t */\n\tprivate constantTimeCompare(a: string, b: string): boolean {\n\t\tif (a.length !== b.length) {\n\t\t\treturn false;\n\t\t}\n\n\t\ttry {\n\t\t\tconst bufferA = Buffer.from(a);\n\t\t\tconst bufferB = Buffer.from(b);\n\t\t\treturn timingSafeEqual(bufferA, bufferB);\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t/**\n\t * Parse expiry string or number to seconds\n\t */\n\tprivate parseExpiry(expiry: ExpiryString | number): number {\n\t\tif (typeof expiry === \"number\") {\n\t\t\treturn expiry;\n\t\t}\n\n\t\tconst match = expiry.match(/^(\\d+)([smhd])$/);\n\t\tif (!match) {\n\t\t\treturn 3600; // default 1 hour\n\t\t}\n\n\t\tconst [, num, unit] = match as [string, string, TimeUnit];\n\t\tconst value = parseInt(num, 10);\n\n\t\tconst multipliers: Record<TimeUnit, number> = {\n\t\t\ts: 1,\n\t\t\tm: 60,\n\t\t\th: 3600,\n\t\t\td: 86400,\n\t\t};\n\n\t\treturn value * multipliers[unit];\n\t}\n}\n\n/**\n * Create a JWT strategy instance\n */\nexport function createJwtStrategy(config: JwtConfig): JwtStrategy {\n\treturn new JwtStrategy(config);\n}\n","/**\n * Authentication Plugin Types\n *\n * Type definitions for the authentication plugin including JWT and Session.\n * Permission/RBAC types are now in @datrix/core.\n */\n\nimport type {\n\tDefaultPermission,\n\tAuthenticatedUser,\n\tDatrixEntry,\n} from \"@datrix/core\";\nimport type { PasswordConfig } from \"./password\";\n\n/**\n * JWT payload (base)\n */\nexport interface JwtPayload {\n\treadonly userId: number;\n\treadonly role: string;\n\treadonly iat: number;\n\treadonly exp: number;\n\treadonly iss?: string;\n\treadonly aud?: string;\n\treadonly [key: string]: unknown;\n}\n\n/**\n * Session store interface\n *\n * Implement this to use a custom session backend (Redis, database, etc.)\n */\nexport interface SessionStore {\n\tget(sessionId: string): Promise<SessionData | undefined>;\n\tset(sessionId: string, data: SessionData): Promise<void>;\n\tdelete(sessionId: string): Promise<void>;\n\tcleanup(): Promise<number>;\n\tclear(): Promise<void>;\n}\n\n/**\n * Session data\n */\nexport interface SessionData {\n\treadonly id: string;\n\treadonly userId: number;\n\treadonly role: string;\n\treadonly createdAt: Date;\n\treadonly expiresAt: Date;\n\treadonly lastAccessedAt: Date;\n\treadonly [key: string]: unknown;\n}\n\n/**\n * JWT algorithm types\n */\nexport type JwtAlgorithm = \"HS256\" | \"HS512\";\n\n/**\n * Time unit for expiration\n */\nexport type TimeUnit = \"s\" | \"m\" | \"h\" | \"d\";\n\n/**\n * Expiry string format (e.g., \"1h\", \"7d\", \"30m\")\n */\nexport type ExpiryString = `${number}${TimeUnit}`;\n\n/**\n * JWT configuration\n */\nexport interface JwtConfig {\n\treadonly secret: string;\n\treadonly expiresIn?: ExpiryString | number; // String like \"1h\" or seconds as number\n\treadonly algorithm?: JwtAlgorithm;\n\treadonly issuer?: string;\n\treadonly audience?: string;\n}\n\n/**\n * JWT token parts\n */\nexport interface JwtToken {\n\treadonly header: JwtHeader;\n\treadonly payload: JwtPayload;\n\treadonly signature: string;\n}\n\n/**\n * JWT header\n */\nexport interface JwtHeader {\n\treadonly alg: JwtAlgorithm;\n\treadonly typ: \"JWT\";\n}\n\n/**\n * Session configuration\n */\nexport interface SessionConfig {\n\t/**\n\t * Session storage backend.\n\t * Pass a custom SessionStore implementation for Redis, database, etc.\n\t * Defaults to in-memory store.\n\t */\n\treadonly store?: \"memory\" | SessionStore;\n\treadonly maxAge?: number; // seconds\n\treadonly checkPeriod?: number; // cleanup interval in seconds\n\treadonly prefix?: string; // session key prefix (only used with default memory store)\n}\n\n/**\n * Authentication configuration\n *\n * When auth is defined in ApiConfig, authentication is enabled.\n * When auth is undefined, authentication is disabled.\n *\n * @template TRole - Union type of valid role names\n */\nexport interface AuthConfig<\n\tTRole extends string = string,\n\tTUser extends DatrixEntry = DatrixEntry,\n> {\n\t/**\n\t * Available roles in the application\n\t */\n\treadonly roles: readonly TRole[];\n\n\t/**\n\t * Default role for new users\n\t */\n\treadonly defaultRole: TRole;\n\n\t/**\n\t * Default permission applied to schemas without explicit permissions\n\t */\n\treadonly defaultPermission?: DefaultPermission<TRole>;\n\n\t/**\n\t * JWT configuration (required if not using session)\n\t */\n\treadonly jwt?: JwtConfig;\n\n\t/**\n\t * Session configuration (required if not using JWT)\n\t */\n\treadonly session?: SessionConfig;\n\n\t/**\n\t * Password hashing configuration (PBKDF2)\n\t * @default { iterations: 100000, keyLength: 64, minLength: 8 }\n\t */\n\treadonly password?: PasswordConfig;\n\n\t/**\n\t * Name for the authentication schema/table\n\t * @default 'authentication'\n\t */\n\treadonly authSchemaName?: string;\n\n\t/**\n\t * User schema configuration\n\t */\n\treadonly userSchema?: {\n\t\t/** User schema name @default 'user' */\n\t\treadonly name?: string;\n\t\t/** Email field name @default 'email' */\n\t\treadonly email?: string;\n\t};\n\n\t/**\n\t * Auth endpoint configuration\n\t */\n\treadonly endpoints?: {\n\t\treadonly login?: string;\n\t\treadonly register?: string;\n\t\treadonly logout?: string;\n\t\treadonly me?: string;\n\t\treadonly forgotPassword?: string;\n\t\treadonly resetPassword?: string;\n\t\treadonly disableRegister?: boolean;\n\t};\n\n\t/**\n\t * Password reset configuration.\n\t * If onForgotPassword is not provided, the forgot-password endpoint will throw.\n\t */\n\treadonly passwordReset?: {\n\t\t/**\n\t\t * Reset token expiry in seconds.\n\t\t * @default 3600 (1 hour)\n\t\t */\n\t\treadonly tokenExpirySeconds?: number;\n\n\t\t/**\n\t\t * Called after a reset token is generated.\n\t\t * Use this to send an email, Telegram message, or any notification.\n\t\t */\n\t\treadonly onForgotPassword?: (\n\t\t\tuser: Omit<AuthenticatedUser<TRole, TUser>, \"password\" | \"passwordSalt\">,\n\t\t\ttoken: string,\n\t\t) => Promise<void>;\n\t};\n}\n\n/**\n * Login credentials\n */\nexport interface LoginCredentials {\n\treadonly email: string;\n\treadonly password: string;\n}\n\n/**\n * Password hash result\n */\nexport interface PasswordHash {\n\treadonly hash: string;\n\treadonly salt: string;\n}\n\n/**\n * Type guard for JWT payload\n */\nexport function isJwtPayload(value: unknown): value is JwtPayload {\n\tif (typeof value !== \"object\" || value === null) {\n\t\treturn false;\n\t}\n\n\tconst obj = value as Record<string, unknown>;\n\n\treturn (\n\t\t\"userId\" in obj &&\n\t\t\"role\" in obj &&\n\t\t\"iat\" in obj &&\n\t\t\"exp\" in obj &&\n\t\ttypeof obj[\"userId\"] === \"number\" &&\n\t\ttypeof obj[\"role\"] === \"string\" &&\n\t\ttypeof obj[\"iat\"] === \"number\" &&\n\t\ttypeof obj[\"exp\"] === \"number\"\n\t);\n}\n\n/**\n * Type guard for session data\n */\nexport function isSessionData(value: unknown): value is SessionData {\n\tif (typeof value !== \"object\" || value === null) {\n\t\treturn false;\n\t}\n\n\tconst obj = value as Record<string, unknown>;\n\n\treturn (\n\t\t\"id\" in obj &&\n\t\t\"userId\" in obj &&\n\t\t\"role\" in obj &&\n\t\t\"createdAt\" in obj &&\n\t\t\"expiresAt\" in obj &&\n\t\t\"lastAccessedAt\" in obj &&\n\t\ttypeof obj[\"id\"] === \"string\" &&\n\t\ttypeof obj[\"userId\"] === \"string\" &&\n\t\ttypeof obj[\"role\"] === \"string\" &&\n\t\tobj[\"createdAt\"] instanceof Date &&\n\t\tobj[\"expiresAt\"] instanceof Date &&\n\t\tobj[\"lastAccessedAt\"] instanceof Date\n\t);\n}\n\n/**\n * Minimum JWT secret length (256 bits = 32 characters for HS256)\n */\nconst MIN_JWT_SECRET_LENGTH = 32;\n\n/**\n * Type guard for auth config\n */\nexport function isAuthConfig(value: unknown): value is AuthConfig<string> {\n\tif (typeof value !== \"object\" || value === null) {\n\t\treturn false;\n\t}\n\n\tconst opts = value as Record<string, unknown>;\n\n\t// roles and defaultRole are required\n\tif (!(\"roles\" in opts) || !Array.isArray(opts[\"roles\"])) {\n\t\treturn false;\n\t}\n\n\tif (!(\"defaultRole\" in opts) || typeof opts[\"defaultRole\"] !== \"string\") {\n\t\treturn false;\n\t}\n\n\t// At least one auth strategy must be configured\n\tif (!(\"jwt\" in opts) && !(\"session\" in opts)) {\n\t\treturn false;\n\t}\n\n\t// Validate JWT config if present\n\tif (\"jwt\" in opts && opts[\"jwt\"] !== undefined) {\n\t\tif (typeof opts[\"jwt\"] !== \"object\" || opts[\"jwt\"] === null) {\n\t\t\treturn false;\n\t\t}\n\t\tconst jwt = opts[\"jwt\"] as Record<string, unknown>;\n\t\tif (\n\t\t\t!(\"secret\" in jwt) ||\n\t\t\ttypeof jwt[\"secret\"] !== \"string\" ||\n\t\t\tjwt[\"secret\"].length < MIN_JWT_SECRET_LENGTH\n\t\t) {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\treturn true;\n}\n\n/**\n * @deprecated Use isAuthConfig instead\n */\nexport const isAuthPluginOptions = isAuthConfig;\n","/**\n * Session Strategy\n *\n * Implements session management with pluggable storage backends.\n * Includes in-memory store by default.\n */\n\nimport { randomBytes } from \"node:crypto\";\nimport type { SessionConfig, SessionData, SessionStore } from \"./types\";\nimport { DEFAULT_API_AUTH_CONFIG } from \"@datrix/core\";\nimport {\n\tthrowSessionCreateError,\n\tthrowSessionNotFound,\n\tthrowSessionExpired,\n} from \"./error-helper\";\nimport { DatrixAuthError } from \"@datrix/core\";\n\n/**\n * Session Strategy\n *\n * Manages user sessions with configurable storage\n */\nexport class SessionStrategy {\n\tprivate readonly store: SessionStore;\n\tprivate readonly maxAge: number; // in seconds\n\tprivate readonly checkPeriod: number; // cleanup interval in seconds\n\tprivate cleanupTimer: NodeJS.Timeout | undefined;\n\n\tconstructor(config: SessionConfig) {\n\t\tthis.maxAge = config.maxAge ?? 86400; // default 24 hours\n\t\tthis.checkPeriod = config.checkPeriod ?? 3600; // default 1 hour\n\n\t\tif (config.store && typeof config.store !== \"string\") {\n\t\t\tthis.store = config.store;\n\t\t} else {\n\t\t\tthis.store = new MemorySessionStore(config.prefix);\n\t\t}\n\t}\n\n\t/**\n\t * Create a new session\n\t */\n\tasync create(\n\t\tuserId: number,\n\t\trole: string,\n\t\tdata?: Record<string, unknown>,\n\t): Promise<SessionData> {\n\t\ttry {\n\t\t\tconst sessionId = this.generateSessionId();\n\t\t\tconst now = new Date();\n\t\t\tconst expiresAt = new Date(now.getTime() + this.maxAge * 1000);\n\n\t\t\tconst sessionData: SessionData = {\n\t\t\t\tid: sessionId,\n\t\t\t\tuserId,\n\t\t\t\trole,\n\t\t\t\tcreatedAt: now,\n\t\t\t\texpiresAt,\n\t\t\t\tlastAccessedAt: now,\n\t\t\t\t...data,\n\t\t\t};\n\n\t\t\tawait this.store.set(sessionId, sessionData);\n\n\t\t\treturn sessionData;\n\t\t} catch (error) {\n\t\t\tif (error instanceof Error && error.name === \"DatrixAuthError\") {\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t\tthrowSessionCreateError(error instanceof Error ? error : undefined);\n\t\t}\n\t}\n\n\t/**\n\t * Get session by ID\n\t */\n\tasync get(sessionId: string): Promise<SessionData> {\n\t\tconst session = await this.store.get(sessionId);\n\n\t\tif (session === undefined) {\n\t\t\tthrowSessionNotFound(sessionId);\n\t\t}\n\n\t\t// Check if session expired\n\t\tconst now = new Date();\n\t\tif (session.expiresAt < now) {\n\t\t\t// Delete expired session\n\t\t\tawait this.store.delete(sessionId);\n\t\t\tthrowSessionExpired(sessionId);\n\t\t}\n\n\t\t// Update last accessed time\n\t\tconst updatedSession: SessionData = {\n\t\t\t...session,\n\t\t\tlastAccessedAt: now,\n\t\t};\n\n\t\tawait this.store.set(sessionId, updatedSession);\n\n\t\treturn updatedSession;\n\t}\n\n\t/**\n\t * Update session data\n\t */\n\tasync update(\n\t\tsessionId: string,\n\t\tdata: Partial<Omit<SessionData, \"id\" | \"createdAt\">>,\n\t): Promise<SessionData> {\n\t\tconst session = await this.get(sessionId);\n\n\t\tconst updatedSession: SessionData = {\n\t\t\t...session,\n\t\t\t...data,\n\t\t\tid: session.id, // Preserve ID\n\t\t\tcreatedAt: session.createdAt, // Preserve creation time\n\t\t};\n\n\t\tawait this.store.set(sessionId, updatedSession);\n\n\t\treturn updatedSession;\n\t}\n\n\t/**\n\t * Delete session\n\t */\n\tasync delete(sessionId: string): Promise<void> {\n\t\tawait this.store.delete(sessionId);\n\t}\n\n\t/**\n\t * Refresh session (extend expiration)\n\t */\n\tasync refresh(sessionId: string): Promise<SessionData> {\n\t\tconst now = new Date();\n\t\tconst expiresAt = new Date(now.getTime() + this.maxAge * 1000);\n\n\t\treturn this.update(sessionId, { expiresAt, lastAccessedAt: now });\n\t}\n\n\t/**\n\t * Validate session (check if exists and not expired)\n\t */\n\tasync validate(sessionId: string): Promise<boolean> {\n\t\ttry {\n\t\t\tawait this.get(sessionId);\n\t\t\treturn true;\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t/**\n\t * Start cleanup timer\n\t */\n\tstartCleanup(): void {\n\t\tif (this.cleanupTimer !== undefined) {\n\t\t\treturn;\n\t\t}\n\n\t\tthis.cleanupTimer = setInterval(() => {\n\t\t\tvoid this.store.cleanup();\n\t\t}, this.checkPeriod * 1000);\n\n\t\t// Don't prevent Node.js from exiting\n\t\tthis.cleanupTimer.unref();\n\t}\n\n\t/**\n\t * Stop cleanup timer\n\t */\n\tstopCleanup(): void {\n\t\tif (this.cleanupTimer !== undefined) {\n\t\t\tclearInterval(this.cleanupTimer);\n\t\t\tthis.cleanupTimer = undefined;\n\t\t}\n\t}\n\n\t/**\n\t * Clear all sessions\n\t */\n\tasync clear(): Promise<void> {\n\t\tawait this.store.clear();\n\t}\n\n\t/**\n\t * Generate secure session ID\n\t */\n\tprivate generateSessionId(): string {\n\t\treturn randomBytes(32).toString(\"hex\");\n\t}\n}\n\n/**\n * In-Memory Session Store\n *\n * Default session store implementation using Map\n */\nexport class MemorySessionStore implements SessionStore {\n\treadonly name = \"memory\" as const;\n\tprivate readonly sessions: Map<string, SessionData> = new Map();\n\tprivate readonly prefix: string;\n\n\tconstructor(prefix: string = DEFAULT_API_AUTH_CONFIG.session.prefix) {\n\t\tthis.prefix = prefix;\n\t}\n\n\tasync get(sessionId: string): Promise<SessionData | undefined> {\n\t\ttry {\n\t\t\tconst key = this.getKey(sessionId);\n\t\t\tconst session = this.sessions.get(key);\n\n\t\t\treturn session;\n\t\t} catch (error) {\n\t\t\tthrow new DatrixAuthError(\"Failed to get session from store\", {\n\t\t\t\tcode: \"SESSION_CREATE_ERROR\",\n\t\t\t\tstrategy: \"session\",\n\t\t\t\tcause: error instanceof Error ? error : undefined,\n\t\t\t});\n\t\t}\n\t}\n\n\tasync set(sessionId: string, data: SessionData): Promise<void> {\n\t\tconst key = this.getKey(sessionId);\n\t\tthis.sessions.set(key, data);\n\t}\n\n\tasync delete(sessionId: string): Promise<void> {\n\t\tconst key = this.getKey(sessionId);\n\t\tthis.sessions.delete(key);\n\t}\n\n\tasync cleanup(): Promise<number> {\n\t\tconst now = new Date();\n\t\tlet deletedCount = 0;\n\n\t\tfor (const [key, session] of this.sessions.entries()) {\n\t\t\tif (session.expiresAt < now) {\n\t\t\t\tthis.sessions.delete(key);\n\t\t\t\tdeletedCount++;\n\t\t\t}\n\t\t}\n\n\t\treturn deletedCount;\n\t}\n\n\tasync clear(): Promise<void> {\n\t\tthis.sessions.clear();\n\t}\n\n\tprivate getKey(sessionId: string): string {\n\t\treturn `${this.prefix}${sessionId}`;\n\t}\n}\n","/**\n * Auth Manager\n *\n * Central authentication manager for API package.\n * Integrates password hashing, JWT, and sessions.\n * Note: Permission checking is now handled by schema-based permissions in middleware/permission.ts\n */\n\nimport { PasswordManager, type PasswordHash } from \"./password\";\nimport { JwtStrategy } from \"./jwt\";\nimport { SessionStrategy } from \"./session\";\nimport { AuthConfig } from \"./types\";\nimport { throwSessionNotConfigured } from \"./error-helper\";\nimport {\n\tAuthContext,\n\tAuthUser,\n\tIAuthManager,\n\tLoginResult,\n\tDatrixEntry,\n} from \"@datrix/core\";\n\n/**\n * Auth Manager\n *\n * Main authentication manager that coordinates all auth components.\n * Note: Permission checking is now schema-based (see middleware/permission.ts)\n */\nexport class AuthManager<\n\tTRole extends string = string,\n\tTUser extends DatrixEntry = DatrixEntry,\n> implements IAuthManager {\n\tprivate readonly passwordManager: PasswordManager;\n\tprivate readonly jwtStrategy: JwtStrategy | undefined;\n\tprivate readonly sessionStrategy: SessionStrategy | undefined;\n\tprivate readonly config: AuthConfig<TRole, TUser>;\n\n\tpublic get authConfig(): AuthConfig<TRole, TUser> {\n\t\treturn this.config;\n\t}\n\n\tconstructor(config: AuthConfig<TRole, TUser>) {\n\t\tthis.config = config;\n\n\t\t// Initialize password manager\n\t\tthis.passwordManager = new PasswordManager(config.password);\n\n\t\t// Initialize JWT strategy if configured\n\t\tif (config.jwt) {\n\t\t\tthis.jwtStrategy = new JwtStrategy(config.jwt);\n\t\t}\n\n\t\t// Initialize session strategy if configured\n\t\tif (config.session) {\n\t\t\tthis.sessionStrategy = new SessionStrategy(config.session);\n\t\t\tthis.sessionStrategy.startCleanup();\n\t\t}\n\t}\n\n\t/**\n\t * Hash password\n\t */\n\tasync hashPassword(password: string): Promise<PasswordHash> {\n\t\treturn this.passwordManager.hash(password);\n\t}\n\n\t/**\n\t * Verify password\n\t */\n\tasync verifyPassword(\n\t\tpassword: string,\n\t\thash: string,\n\t\tsalt: string,\n\t): Promise<boolean> {\n\t\treturn this.passwordManager.verify(password, hash, salt);\n\t}\n\n\t/**\n\t * Login user and create token/session\n\t */\n\tasync login(\n\t\tuser: AuthUser,\n\t\toptions: { createToken?: boolean; createSession?: boolean } = {},\n\t): Promise<LoginResult> {\n\t\tconst { createToken = true, createSession = true } = options;\n\n\t\tlet token: string | undefined = undefined;\n\t\tlet sessionId: string | undefined = undefined;\n\n\t\t// Create JWT token if enabled and requested\n\t\tif (this.jwtStrategy && createToken) {\n\t\t\ttoken = this.jwtStrategy.sign({\n\t\t\t\tuserId: user.id,\n\t\t\t\trole: user.role,\n\t\t\t});\n\t\t}\n\n\t\t// Create session if enabled and requested\n\t\tif (this.sessionStrategy && createSession) {\n\t\t\tconst sessionData = await this.sessionStrategy.create(user.id, user.role);\n\t\t\tsessionId = sessionData.id;\n\t\t}\n\n\t\tconst result: LoginResult = {\n\t\t\tuser,\n\t\t\t...(token !== undefined && { token }),\n\t\t\t...(sessionId !== undefined && { sessionId }),\n\t\t};\n\n\t\treturn result;\n\t}\n\n\t/**\n\t * Logout user (destroy session)\n\t */\n\tasync logout(sessionId: string): Promise<void> {\n\t\tif (!this.sessionStrategy) {\n\t\t\tthrowSessionNotConfigured();\n\t\t}\n\n\t\tawait this.sessionStrategy.delete(sessionId);\n\t}\n\n\t/**\n\t * Authenticate request (extract and verify token/session)\n\t */\n\tasync authenticate(request: Request): Promise<AuthContext | null> {\n\t\t// Try JWT first\n\t\tconst token = this.extractToken(request);\n\t\tif (token && this.jwtStrategy) {\n\t\t\ttry {\n\t\t\t\tconst payload = this.jwtStrategy.verify(token);\n\t\t\t\treturn {\n\t\t\t\t\tuser: {\n\t\t\t\t\t\tid: payload.userId,\n\t\t\t\t\t\temail: \"\", // Will be fetched from DB if needed\n\t\t\t\t\t\trole: payload.role,\n\t\t\t\t\t},\n\t\t\t\t\ttoken,\n\t\t\t\t};\n\t\t\t} catch {\n\t\t\t\t// JWT verification failed, continue to session\n\t\t\t}\n\t\t}\n\n\t\t// Try session\n\t\tconst sessionId = this.extractSessionId(request);\n\t\tif (sessionId && this.sessionStrategy) {\n\t\t\ttry {\n\t\t\t\tconst session = await this.sessionStrategy.get(sessionId);\n\t\t\t\treturn {\n\t\t\t\t\tuser: {\n\t\t\t\t\t\tid: session.userId,\n\t\t\t\t\t\temail: \"\",\n\t\t\t\t\t\trole: session.role,\n\t\t\t\t\t},\n\t\t\t\t\tsessionId,\n\t\t\t\t};\n\t\t\t} catch {\n\t\t\t\t// Session not found or expired\n\t\t\t}\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t/**\n\t * Extract JWT token from request headers\n\t */\n\tprivate extractToken(request: Request): string | null {\n\t\tconst authHeader = request.headers.get(\"authorization\");\n\t\tif (!authHeader || !authHeader.startsWith(\"Bearer \")) {\n\t\t\treturn null;\n\t\t}\n\n\t\treturn authHeader.slice(7); // Remove 'Bearer ' prefix\n\t}\n\n\t/**\n\t * Extract session ID from request cookies\n\t */\n\tprivate extractSessionId(request: Request): string | null {\n\t\tconst cookieHeader = request.headers.get(\"cookie\");\n\t\tif (!cookieHeader) {\n\t\t\treturn null;\n\t\t}\n\n\t\t// Parse cookies\n\t\tconst cookies = cookieHeader.split(\";\").reduce(\n\t\t\t(acc, cookie) => {\n\t\t\t\tconst [key, value] = cookie.trim().split(\"=\");\n\t\t\t\tif (key && value) {\n\t\t\t\t\tacc[key] = value;\n\t\t\t\t}\n\t\t\t\treturn acc;\n\t\t\t},\n\t\t\t{} as Record<string, string>,\n\t\t);\n\n\t\treturn cookies[\"sessionId\"] ?? null;\n\t}\n\n\t/**\n\t * Get JWT strategy (for advanced usage)\n\t */\n\tgetJwtStrategy(): JwtStrategy | undefined {\n\t\treturn this.jwtStrategy;\n\t}\n\n\t/**\n\t * Get session strategy (for advanced usage)\n\t */\n\tgetSessionStrategy(): SessionStrategy | undefined {\n\t\treturn this.sessionStrategy;\n\t}\n\n\t/**\n\t * Cleanup resources\n\t */\n\tasync destroy(): Promise<void> {\n\t\t// Stop session cleanup timer\n\t\tif (this.sessionStrategy) {\n\t\t\tthis.sessionStrategy.stopCleanup();\n\t\t}\n\n\t\t// Clear sessions\n\t\tif (this.sessionStrategy) {\n\t\t\tawait this.sessionStrategy.clear();\n\t\t}\n\t}\n}\n","/**\n * Auth Handlers\n *\n * HTTP handlers for authentication endpoints:\n * - POST /auth/register - Register new user\n * - POST /auth/login - Login user\n * - POST /auth/logout - Logout user\n * - GET /auth/me - Get current user\n *\n * Authentication data is stored in the 'authentication' table,\n * separate from user business data in the 'user' table.\n */\n\nimport type { Datrix } from \"@datrix/core\";\nimport { DEFAULT_API_AUTH_CONFIG } from \"@datrix/core\";\nimport { AuthManager } from \"../auth/manager\";\nimport type { AuthConfig } from \"../auth/types\";\nimport { jsonResponse, extractSessionId, datrixErrorResponse } from \"./utils\";\nimport { authError } from \"../errors/auth-error\";\nimport { handlerError } from \"../errors/api-error\";\nimport { DatrixError } from \"@datrix/core\";\nimport { AuthenticatedUser } from \"@datrix/core\";\nimport { DatrixEntry } from \"@datrix/core\";\nimport { AuthUser } from \"@datrix/core\";\nimport { FallbackValue } from \"@datrix/core\";\nimport { FallbackInput } from \"@datrix/core\";\n\n/**\n * Auth Handler Configuration\n */\nexport interface AuthHandlerConfig<\n\tTRole extends string = string,\n\tTUser extends DatrixEntry = DatrixEntry,\n> {\n\treadonly datrix: Datrix;\n\treadonly authManager: AuthManager<TRole, TUser>;\n\treadonly authConfig: AuthConfig<TRole, TUser>;\n}\n\n/**\n * Auth Handlers Factory\n *\n * Creates authentication endpoint handlers\n */\nexport function createAuthHandlers<\n\tTRole extends string = string,\n\tTUser extends DatrixEntry = DatrixEntry,\n>(config: AuthHandlerConfig<TRole, TUser>) {\n\tconst { datrix, authManager, authConfig } = config;\n\n\tconst userSchemaName = authConfig.userSchema?.name ?? \"user\";\n\tconst authSchemaName = authConfig.authSchemaName ?? \"authentication\";\n\tconst userEmailField = authConfig.userSchema?.email ?? \"email\";\n\tconst defaultRole = authConfig.defaultRole;\n\n\t/**\n\t * POST /auth/register - Register new user\n\t */\n\tasync function register(request: Request): Promise<Response> {\n\t\ttry {\n\t\t\tif (authConfig.endpoints?.disableRegister) {\n\t\t\t\tthrow handlerError.permissionDenied(\"Registration is disabled\");\n\t\t\t}\n\n\t\t\tconst body = (await request.json()) as FallbackValue;\n\t\t\tconst { email, password, ...extraData } = body;\n\n\t\t\tif (!email || typeof email !== \"string\") {\n\t\t\t\tthrow handlerError.invalidBody(\"Email is required\");\n\t\t\t}\n\n\t\t\tif (!password || typeof password !== \"string\") {\n\t\t\t\tthrow handlerError.invalidBody(\"Password is required\");\n\t\t\t}\n\n\t\t\tconst existingAuth = await datrix.raw.findOne<AuthenticatedUser>(\n\t\t\t\tauthSchemaName,\n\t\t\t\t{ email: email },\n\t\t\t);\n\n\t\t\tif (existingAuth) {\n\t\t\t\tthrow handlerError.conflict(\"User with this email already exists\");\n\t\t\t}\n\n\t\t\tconst { hash, salt } = await authManager.hashPassword(password);\n\n\t\t\tconst userData = {\n\t\t\t\t[userEmailField]: email,\n\t\t\t\t...extraData,\n\t\t\t} as FallbackInput;\n\n\t\t\tlet user: DatrixEntry;\n\t\t\ttry {\n\t\t\t\tconst createdUser = await datrix.raw.create(userSchemaName, userData);\n\n\t\t\t\tif (!createdUser) {\n\t\t\t\t\tthrow handlerError.internalError(\"Failed to create user record\");\n\t\t\t\t}\n\t\t\t\tuser = createdUser;\n\t\t\t} catch (error) {\n\t\t\t\tif (error instanceof DatrixError) {\n\t\t\t\t\tthrow error;\n\t\t\t\t}\n\t\t\t\tconst message =\n\t\t\t\t\terror instanceof Error ? error.message : \"Failed to create user\";\n\t\t\t\tthrow handlerError.invalidBody(message);\n\t\t\t}\n\n\t\t\tconst authData = {\n\t\t\t\tuser: { set: [{ id: user.id }] },\n\t\t\t\temail: email,\n\t\t\t\tpassword: hash,\n\t\t\t\tpasswordSalt: salt,\n\t\t\t\trole: defaultRole,\n\t\t\t};\n\n\t\t\tconst authRecord = await datrix.raw.create<AuthenticatedUser>(\n\t\t\t\tauthSchemaName,\n\t\t\t\tauthData,\n\t\t\t);\n\n\t\t\tif (!authRecord) {\n\t\t\t\tawait datrix.raw.delete(userSchemaName, user.id);\n\t\t\t\tthrow handlerError.internalError(\n\t\t\t\t\t\"Failed to create authentication record\",\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst authUser: AuthUser = {\n\t\t\t\tid: authRecord.id,\n\t\t\t\temail: authRecord.email,\n\t\t\t\trole: authRecord.role,\n\t\t\t};\n\n\t\t\tconst loginResult = await authManager.login(authUser);\n\n\t\t\tconst responseBody = {\n\t\t\t\tdata: {\n\t\t\t\t\tuser: authUser,\n\t\t\t\t\ttoken: loginResult.token,\n\t\t\t\t\tsessionId: loginResult.sessionId,\n\t\t\t\t},\n\t\t\t};\n\n\t\t\tif (loginResult.sessionId) {\n\t\t\t\treturn new Response(JSON.stringify(responseBody), {\n\t\t\t\t\tstatus: 201,\n\t\t\t\t\theaders: {\n\t\t\t\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t\t\t\t\"Set-Cookie\": `sessionId=${loginResult.sessionId}; HttpOnly; Path=/; Max-Age=86400; SameSite=Strict`,\n\t\t\t\t\t},\n\t\t\t\t});\n\t\t\t}\n\n\t\t\treturn jsonResponse(responseBody, 201);\n\t\t} catch (error) {\n\t\t\tif (error instanceof DatrixError) {\n\t\t\t\treturn datrixErrorResponse(error);\n\t\t\t}\n\t\t\tconst message =\n\t\t\t\terror instanceof Error ? error.message : \"Internal server error\";\n\t\t\treturn datrixErrorResponse(\n\t\t\t\thandlerError.internalError(\n\t\t\t\t\tmessage,\n\t\t\t\t\terror instanceof Error ? error : undefined,\n\t\t\t\t),\n\t\t\t);\n\t\t}\n\t}\n\n\t/**\n\t * POST /auth/login - Login user\n\t */\n\tasync function login(request: Request): Promise<Response> {\n\t\ttry {\n\t\t\tconst body = await request.json();\n\t\t\tconst { email, password } = body as Record<string, string>;\n\n\t\t\tif (!email || typeof email !== \"string\") {\n\t\t\t\tthrow handlerError.invalidBody(\"Email is required\");\n\t\t\t}\n\n\t\t\tif (!password || typeof password !== \"string\") {\n\t\t\t\tthrow handlerError.invalidBody(\"Password is required\");\n\t\t\t}\n\n\t\t\tconst authRecord = await datrix.raw.findOne<AuthenticatedUser>(\n\t\t\t\tauthSchemaName,\n\t\t\t\t{ email: email },\n\t\t\t);\n\n\t\t\tif (!authRecord) {\n\t\t\t\tthrow authError.invalidCredentials();\n\t\t\t}\n\n\t\t\tconst isValid = await authManager.verifyPassword(\n\t\t\t\tpassword,\n\t\t\t\tauthRecord.password,\n\t\t\t\tauthRecord.passwordSalt,\n\t\t\t);\n\n\t\t\tif (!isValid) {\n\t\t\t\tthrow authError.invalidCredentials();\n\t\t\t}\n\n\t\t\tconst authUser: AuthUser = {\n\t\t\t\tid: authRecord.id,\n\t\t\t\temail: authRecord.email,\n\t\t\t\trole: authRecord.role,\n\t\t\t};\n\n\t\t\tconst loginResult = await authManager.login(authUser);\n\n\t\t\tconst responseBody = {\n\t\t\t\tdata: {\n\t\t\t\t\tuser: authUser,\n\t\t\t\t\ttoken: loginResult.token,\n\t\t\t\t\tsessionId: loginResult.sessionId,\n\t\t\t\t},\n\t\t\t};\n\n\t\t\tif (loginResult.sessionId) {\n\t\t\t\treturn new Response(JSON.stringify(responseBody), {\n\t\t\t\t\tstatus: 200,\n\t\t\t\t\theaders: {\n\t\t\t\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t\t\t\t\"Set-Cookie\": `sessionId=${loginResult.sessionId}; HttpOnly; Path=/; Max-Age=86400; SameSite=Strict`,\n\t\t\t\t\t},\n\t\t\t\t});\n\t\t\t}\n\n\t\t\treturn jsonResponse(responseBody);\n\t\t} catch (error) {\n\t\t\tif (error instanceof DatrixError) {\n\t\t\t\treturn datrixErrorResponse(error);\n\t\t\t}\n\t\t\tconst message =\n\t\t\t\terror instanceof Error ? error.message : \"Internal server error\";\n\t\t\treturn datrixErrorResponse(\n\t\t\t\thandlerError.internalError(\n\t\t\t\t\tmessage,\n\t\t\t\t\terror instanceof Error ? error : undefined,\n\t\t\t\t),\n\t\t\t);\n\t\t}\n\t}\n\n\t/**\n\t * POST /auth/logout - Logout user\n\t */\n\tasync function logout(request: Request): Promise<Response> {\n\t\ttry {\n\t\t\tconst sessionId = extractSessionId(request);\n\n\t\t\tif (!sessionId) {\n\t\t\t\tthrow handlerError.invalidBody(\"No session found\");\n\t\t\t}\n\n\t\t\tawait authManager.logout(sessionId);\n\n\t\t\treturn new Response(JSON.stringify({ data: { success: true } }), {\n\t\t\t\tstatus: 200,\n\t\t\t\theaders: {\n\t\t\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t\t\t\"Set-Cookie\":\n\t\t\t\t\t\t\"sessionId=; HttpOnly; Path=/; Max-Age=0; Expires=Thu, 01 Jan 1970 00:00:00 GMT; SameSite=Strict\",\n\t\t\t\t},\n\t\t\t});\n\t\t} catch (error) {\n\t\t\tif (error instanceof DatrixError) {\n\t\t\t\treturn datrixErrorResponse(error);\n\t\t\t}\n\t\t\tconst message =\n\t\t\t\terror instanceof Error ? error.message : \"Internal server error\";\n\t\t\treturn datrixErrorResponse(\n\t\t\t\thandlerError.internalError(\n\t\t\t\t\tmessage,\n\t\t\t\t\terror instanceof Error ? error : undefined,\n\t\t\t\t),\n\t\t\t);\n\t\t}\n\t}\n\n\t/**\n\t * GET /auth/me - Get current user\n\t */\n\tasync function me(request: Request): Promise<Response> {\n\t\ttry {\n\t\t\tconst authContext = await authManager.authenticate(request);\n\n\t\t\tif (!authContext || !authContext.user) {\n\t\t\t\tthrow authError.invalidToken();\n\t\t\t}\n\n\t\t\tconst authenticatedUser = await datrix.raw.findById<AuthenticatedUser>(\n\t\t\t\tauthSchemaName,\n\t\t\t\tauthContext.user.id,\n\t\t\t\t{ populate: { user: \"*\" } },\n\t\t\t);\n\n\t\t\tif (!authenticatedUser) {\n\t\t\t\tthrow handlerError.recordNotFound(\n\t\t\t\t\tuserSchemaName,\n\t\t\t\t\tString(authContext.user.id),\n\t\t\t\t);\n\t\t\t}\n\n\t\t\treturn jsonResponse({ data: authenticatedUser });\n\t\t} catch (error) {\n\t\t\tif (error instanceof DatrixError) {\n\t\t\t\treturn datrixErrorResponse(error);\n\t\t\t}\n\t\t\tconst message =\n\t\t\t\terror instanceof Error ? error.message : \"Internal server error\";\n\t\t\treturn datrixErrorResponse(\n\t\t\t\thandlerError.internalError(\n\t\t\t\t\tmessage,\n\t\t\t\t\terror instanceof Error ? error : undefined,\n\t\t\t\t),\n\t\t\t);\n\t\t}\n\t}\n\n\t/**\n\t * POST /auth/forgot-password - Request password reset token\n\t */\n\tasync function forgotPassword(request: Request): Promise<Response> {\n\t\ttry {\n\t\t\tconst onForgotPassword = authConfig.passwordReset?.onForgotPassword;\n\n\t\t\tif (!onForgotPassword) {\n\t\t\t\tthrow handlerError.permissionDenied(\"Password reset is not configured\");\n\t\t\t}\n\n\t\t\tconst body = (await request.json()) as Record<string, unknown>;\n\t\t\tconst { email } = body;\n\n\t\t\tif (!email || typeof email !== \"string\") {\n\t\t\t\tthrow handlerError.invalidBody(\"Email is required\");\n\t\t\t}\n\n\t\t\tconst authRecord = await datrix.raw.findOne<\n\t\t\t\tAuthenticatedUser<TRole, TUser>\n\t\t\t>(\n\t\t\t\tauthSchemaName,\n\t\t\t\t{ email },\n\t\t\t\t{\n\t\t\t\t\tpopulate: true,\n\t\t\t\t\tselect: [\"email\", \"role\", \"resetToken\", \"resetTokenExpiry\"],\n\t\t\t\t},\n\t\t\t);\n\n\t\t\tif (!authRecord) {\n\t\t\t\treturn jsonResponse({ data: { success: true } });\n\t\t\t}\n\n\t\t\tconst tokenBytes = new Uint8Array(32);\n\t\t\tcrypto.getRandomValues(tokenBytes);\n\t\t\tconst token = Array.from(tokenBytes)\n\t\t\t\t.map((b) => b.toString(16).padStart(2, \"0\"))\n\t\t\t\t.join(\"\");\n\n\t\t\tconst expirySeconds =\n\t\t\t\tauthConfig.passwordReset?.tokenExpirySeconds ??\n\t\t\t\tDEFAULT_API_AUTH_CONFIG.passwordReset.tokenExpirySeconds;\n\n\t\t\tconst expiry = new Date(Date.now() + expirySeconds * 1000);\n\n\t\t\tawait datrix.raw.update(authSchemaName, authRecord.id, {\n\t\t\t\tresetToken: token,\n\t\t\t\tresetTokenExpiry: expiry,\n\t\t\t});\n\n\t\t\tawait onForgotPassword(authRecord, token);\n\n\t\t\treturn jsonResponse({ data: { success: true } });\n\t\t} catch (error) {\n\t\t\tif (error instanceof DatrixError) {\n\t\t\t\treturn datrixErrorResponse(error);\n\t\t\t}\n\t\t\tconst message =\n\t\t\t\terror instanceof Error ? error.message : \"Internal server error\";\n\t\t\treturn datrixErrorResponse(\n\t\t\t\thandlerError.internalError(\n\t\t\t\t\tmessage,\n\t\t\t\t\terror instanceof Error ? error : undefined,\n\t\t\t\t),\n\t\t\t);\n\t\t}\n\t}\n\n\t/**\n\t * POST /auth/reset-password - Reset password using token\n\t */\n\tasync function resetPassword(request: Request): Promise<Response> {\n\t\ttry {\n\t\t\tconst body = (await request.json()) as Record<string, unknown>;\n\t\t\tconst { token, password } = body;\n\n\t\t\tif (!token || typeof token !== \"string\") {\n\t\t\t\tthrow handlerError.invalidBody(\"Token is required\");\n\t\t\t}\n\n\t\t\tif (!password || typeof password !== \"string\") {\n\t\t\t\tthrow handlerError.invalidBody(\"Password is required\");\n\t\t\t}\n\n\t\t\tconst authRecord = await datrix.raw.findOne<AuthenticatedUser>(\n\t\t\t\tauthSchemaName,\n\t\t\t\t{ resetToken: token },\n\t\t\t);\n\n\t\t\tif (\n\t\t\t\t!authRecord ||\n\t\t\t\t!authRecord.resetTokenExpiry ||\n\t\t\t\tnew Date(authRecord.resetTokenExpiry) < new Date()\n\t\t\t) {\n\t\t\t\tthrow handlerError.invalidBody(\"Invalid or expired reset token\");\n\t\t\t}\n\n\t\t\tconst { hash, salt } = await authManager.hashPassword(password);\n\n\t\t\tawait datrix.raw.update(authSchemaName, authRecord.id, {\n\t\t\t\tpassword: hash,\n\t\t\t\tpasswordSalt: salt,\n\t\t\t\tresetToken: null,\n\t\t\t\tresetTokenExpiry: null,\n\t\t\t});\n\n\t\t\treturn jsonResponse({ data: { success: true } });\n\t\t} catch (error) {\n\t\t\tif (error instanceof DatrixError) {\n\t\t\t\treturn datrixErrorResponse(error);\n\t\t\t}\n\t\t\tconst message =\n\t\t\t\terror instanceof Error ? error.message : \"Internal server error\";\n\t\t\treturn datrixErrorResponse(\n\t\t\t\thandlerError.internalError(\n\t\t\t\t\tmessage,\n\t\t\t\t\terror instanceof Error ? error : undefined,\n\t\t\t\t),\n\t\t\t);\n\t\t}\n\t}\n\n\treturn { register, login, logout, me, forgotPassword, resetPassword };\n}\n\n/**\n * Create unified auth handler (handles routing internally)\n */\nexport function createUnifiedAuthHandler<\n\tTRole extends string = string,\n\tTUser extends DatrixEntry = DatrixEntry,\n>(config: AuthHandlerConfig<TRole, TUser>, apiPrefix: string = \"/api\") {\n\tconst handlers = createAuthHandlers(config);\n\tconst { authConfig } = config;\n\n\tconst endpoints = {\n\t\tregister:\n\t\t\tauthConfig.endpoints?.register ??\n\t\t\tDEFAULT_API_AUTH_CONFIG.endpoints.register,\n\t\tlogin:\n\t\t\tauthConfig.endpoints?.login ?? DEFAULT_API_AUTH_CONFIG.endpoints.login,\n\t\tlogout:\n\t\t\tauthConfig.endpoints?.logout ?? DEFAULT_API_AUTH_CONFIG.endpoints.logout,\n\t\tme: authConfig.endpoints?.me ?? DEFAULT_API_AUTH_CONFIG.endpoints.me,\n\t\tforgotPassword:\n\t\t\tauthConfig.endpoints?.forgotPassword ??\n\t\t\tDEFAULT_API_AUTH_CONFIG.endpoints.forgotPassword,\n\t\tresetPassword:\n\t\t\tauthConfig.endpoints?.resetPassword ??\n\t\t\tDEFAULT_API_AUTH_CONFIG.endpoints.resetPassword,\n\t};\n\n\treturn async function authHandler(request: Request): Promise<Response> {\n\t\tconst url = new URL(request.url);\n\t\tconst path = url.pathname.slice(apiPrefix.length);\n\t\tconst method = request.method;\n\n\t\tif (path === endpoints.register && method === \"POST\") {\n\t\t\treturn handlers.register(request);\n\t\t}\n\n\t\tif (path === endpoints.login && method === \"POST\") {\n\t\t\treturn handlers.login(request);\n\t\t}\n\n\t\tif (path === endpoints.logout && method === \"POST\") {\n\t\t\treturn handlers.logout(request);\n\t\t}\n\n\t\tif (path === endpoints.me && method === \"GET\") {\n\t\t\treturn handlers.me(request);\n\t\t}\n\n\t\tif (path === endpoints.forgotPassword && method === \"POST\") {\n\t\t\treturn handlers.forgotPassword(request);\n\t\t}\n\n\t\tif (path === endpoints.resetPassword && method === \"POST\") {\n\t\t\treturn handlers.resetPassword(request);\n\t\t}\n\n\t\treturn datrixErrorResponse(\n\t\t\thandlerError.recordNotFound(\"Auth Route\", url.pathname),\n\t\t);\n\t};\n}\n","/**\n * Handler Utilities\n *\n * Shared utility functions for handlers\n */\n\nimport { ParserError } from \"@datrix/core\";\nimport { DatrixError, DatrixValidationError } from \"@datrix/core\";\nimport { DatrixApiError } from \"../errors/api-error\";\n\n/**\n * Create JSON response\n */\nexport function jsonResponse(data: unknown, status = 200): Response {\n\treturn new Response(JSON.stringify(data), {\n\t\tstatus,\n\t\theaders: { \"Content-Type\": \"application/json\" },\n\t});\n}\n\n/**\n * Generic DatrixError to Response converter\n * Handles ApiError (with status), DatrixValidationError (400), and base DatrixError\n */\nexport function datrixErrorResponse(\n\terror: DatrixApiError | DatrixError,\n): Response {\n\tlet status = 400;\n\n\tif (error instanceof DatrixApiError) {\n\t\tstatus = error.status;\n\t} else if (error instanceof DatrixValidationError) {\n\t\tstatus = 400;\n\t}\n\n\tconst serialized = error.toJSON();\n\n\treturn jsonResponse(\n\t\t{\n\t\t\terror: {\n\t\t\t\t...serialized,\n\t\t\t\ttype: error.name,\n\t\t\t},\n\t\t},\n\t\tstatus,\n\t);\n}\n\n/**\n * Create error response (Legacy support - will be phased out)\n * Use ApiError/datrixErrorResponse for new code\n */\nexport function errorResponse(\n\tmessage: string,\n\tcode: string,\n\tstatus = 500,\n): Response {\n\treturn jsonResponse({ error: { message, code } }, status);\n}\n\n/**\n * Create detailed parser error response\n * @deprecated Use datrixErrorResponse instead\n */\nexport function parserErrorResponse(error: ParserError): Response {\n\treturn datrixErrorResponse(error);\n}\n\n/**\n * Extract session ID from request cookies\n */\nexport function extractSessionId(request: Request): string | null {\n\tconst cookieHeader = request.headers.get(\"cookie\");\n\tif (!cookieHeader) return null;\n\n\tconst match = cookieHeader.match(/sessionId=([^;]+)/);\n\treturn match ? match[1]! : null;\n}\n","/**\n * API Error System\n *\n * Provides a unified error structure for the API package,\n * extending DatrixError with HTTP status handling and helpful context.\n */\n\nimport { DatrixError } from \"@datrix/core\";\n\n/**\n * Base API Error Class\n *\n * All API-specific errors should inherit from this class\n * or be created via its static helpers.\n */\nexport class DatrixApiError extends DatrixError {\n\t/** HTTP status code associated with this error */\n\tstatus: number;\n\n\tconstructor(message: string, options: ApiErrorOptions) {\n\t\tsuper(message, {\n\t\t\tcode: options.code,\n\t\t\toperation: options.operation || \"api:handler\",\n\t\t\t...(options.context && { context: options.context }),\n\t\t\t...(options.suggestion && { suggestion: options.suggestion }),\n\t\t\t...(options.expected && { expected: options.expected }),\n\t\t\t...(options.received !== undefined && { received: options.received }),\n\t\t\t...(options.cause && { cause: options.cause }),\n\t\t});\n\n\t\tthis.status = options.status || 500;\n\t}\n\n\t/**\n\t * Override toJSON to include status\n\t */\n\toverride toJSON() {\n\t\treturn {\n\t\t\t...super.toJSON(),\n\t\t\tstatus: this.status,\n\t\t};\n\t}\n}\n\nexport interface ApiErrorOptions {\n\tcode: string;\n\tstatus: number;\n\toperation?: string;\n\tcontext?: Record<string, unknown>;\n\tsuggestion?: string;\n\texpected?: string;\n\treceived?: unknown;\n\tcause?: Error;\n}\n\n/**\n * Handler Error Helpers\n *\n * Centralized error creation for routine API handlers.\n */\nexport const handlerError = {\n\tschemaNotFound(\n\t\ttableName: string,\n\t\tavailableModels?: string[],\n\t): DatrixApiError {\n\t\treturn new DatrixApiError(`Model not found for table: ${tableName}`, {\n\t\t\tcode: \"SCHEMA_NOT_FOUND\",\n\t\t\tstatus: 404,\n\t\t\tcontext: { tableName, availableModels },\n\t\t\tsuggestion:\n\t\t\t\t\"Check if the table name is correct and the schema is properly defined.\",\n\t\t});\n\t},\n\n\tmodelNotSpecified(): DatrixApiError {\n\t\treturn new DatrixApiError(\"Model not specified in the request URL\", {\n\t\t\tcode: \"MODEL_NOT_SPECIFIED\",\n\t\t\tstatus: 400,\n\t\t\tsuggestion: \"Ensure the URL includes the model name (e.g., /api/users).\",\n\t\t});\n\t},\n\n\trecordNotFound(modelName: string, id: number | string): DatrixApiError {\n\t\treturn new DatrixApiError(`${modelName} record not found with ID: ${id}`, {\n\t\t\tcode: \"NOT_FOUND\",\n\t\t\tstatus: 404,\n\t\t\tcontext: { modelName, id },\n\t\t\tsuggestion: \"Verify the ID is correct or if the record has been deleted.\",\n\t\t});\n\t},\n\n\tinvalidBody(reason?: string): DatrixApiError {\n\t\treturn new DatrixApiError(\n\t\t\treason ? `Invalid request body: ${reason}` : \"Invalid request body\",\n\t\t\t{\n\t\t\t\tcode: \"INVALID_BODY\",\n\t\t\t\tstatus: 400,\n\t\t\t\tcontext: { reason },\n\t\t\t\tsuggestion:\n\t\t\t\t\t\"Ensure the request body is a valid JSON object and contains all required fields.\",\n\t\t\t},\n\t\t);\n\t},\n\n\tmissingId(operation: string): DatrixApiError {\n\t\treturn new DatrixApiError(`ID is required for ${operation}`, {\n\t\t\tcode: \"MISSING_ID\",\n\t\t\tstatus: 400,\n\t\t\tsuggestion: `Provide a valid ID in the URL for the ${operation} operation.`,\n\t\t});\n\t},\n\n\tmethodNotAllowed(method: string): DatrixApiError {\n\t\treturn new DatrixApiError(\n\t\t\t`HTTP Method ${method} is not allowed for this route`,\n\t\t\t{\n\t\t\t\tcode: \"METHOD_NOT_ALLOWED\",\n\t\t\t\tstatus: 405,\n\t\t\t\tcontext: { method },\n\t\t\t\tsuggestion:\n\t\t\t\t\t\"Check the API documentation for supported methods on this endpoint.\",\n\t\t\t},\n\t\t);\n\t},\n\n\tpermissionDenied(\n\t\treason: string,\n\t\tcontext?: Record<string, unknown>,\n\t): DatrixApiError {\n\t\treturn new DatrixApiError(\"Permission denied\", {\n\t\t\tcode: \"FORBIDDEN\",\n\t\t\tstatus: 403,\n\t\t\tcontext: { reason, ...context },\n\t\t\tsuggestion: \"Check your permissions or contact an administrator.\",\n\t\t});\n\t},\n\n\tunauthorized(reason?: string): DatrixApiError {\n\t\treturn new DatrixApiError(\"Unauthorized access\", {\n\t\t\tcode: \"UNAUTHORIZED\",\n\t\t\tstatus: 401,\n\t\t\tcontext: { reason },\n\t\t\tsuggestion: \"Provide valid authentication credentials.\",\n\t\t});\n\t},\n\n\tinternalError(message: string, cause?: Error): DatrixApiError {\n\t\treturn new DatrixApiError(message, {\n\t\t\tcode: \"INTERNAL_ERROR\",\n\t\t\tstatus: 500,\n\t\t\t...(cause && { cause }),\n\t\t});\n\t},\n\n\tconflict(reason: string, context?: Record<string, unknown>): DatrixApiError {\n\t\treturn new DatrixApiError(reason, {\n\t\t\tcode: \"CONFLICT\",\n\t\t\tstatus: 409,\n\t\t\t...(context && { context }),\n\t\t\tsuggestion:\n\t\t\t\t\"Ensure the resource you are trying to create does not already exist.\",\n\t\t});\n\t},\n};\n","/**\n * Auth Specific Errors\n */\n\nimport { DatrixApiError } from \"./api-error\";\n\n/**\n * Auth Error Helper\n */\nexport const authError = {\n\tinvalidCredentials(): DatrixApiError {\n\t\treturn new DatrixApiError(\"Invalid email or password\", {\n\t\t\tcode: \"INVALID_CREDENTIALS\",\n\t\t\tstatus: 401,\n\t\t\tsuggestion: \"Please check your email and password and try again.\",\n\t\t});\n\t},\n\n\tinvalidToken(reason?: string): DatrixApiError {\n\t\treturn new DatrixApiError(\"Invalid or expired authentication token\", {\n\t\t\tcode: \"INVALID_TOKEN\",\n\t\t\tstatus: 401,\n\t\t\tcontext: { reason },\n\t\t\tsuggestion: \"Please log in again to obtain a new session.\",\n\t\t});\n\t},\n\n\tmissingToken(): DatrixApiError {\n\t\treturn new DatrixApiError(\"Authentication token is missing\", {\n\t\t\tcode: \"MISSING_TOKEN\",\n\t\t\tstatus: 401,\n\t\t\tsuggestion:\n\t\t\t\t\"Include an Authorization header or a session cookie in your request.\",\n\t\t});\n\t},\n\n\tsessionExpired(): DatrixApiError {\n\t\treturn new DatrixApiError(\"Your session has expired\", {\n\t\t\tcode: \"SESSION_EXPIRED\",\n\t\t\tstatus: 401,\n\t\t\tsuggestion: \"Log in again to continue using the application.\",\n\t\t});\n\t},\n\n\taccountLocked(reason?: string): DatrixApiError {\n\t\treturn new DatrixApiError(\"This account has been locked\", {\n\t\t\tcode: \"ACCOUNT_LOCKED\",\n\t\t\tstatus: 403,\n\t\t\tcontext: { reason },\n\t\t\tsuggestion: \"Contact support to unlock your account.\",\n\t\t});\n\t},\n};\n","/**\n * Permission Middleware\n *\n * Schema-based permission checking with support for:\n * - Boolean permissions (true = everyone, false = no one)\n * - Role arrays (['admin', 'editor'])\n * - Permission functions ((ctx) => boolean)\n * - Mixed arrays with OR logic (['admin', (ctx) => ctx.user?.id === ctx.record?.authorId])\n */\n\nimport type { DatrixEntry, SchemaDefinition } from \"@datrix/core\";\nimport type {\n\tPermissionAction,\n\tPermissionValue,\n\tPermissionContext,\n\tPermissionFn,\n\tSchemaPermission,\n\tDefaultPermission,\n\tPermissionCheckResult,\n\tFieldPermission,\n\tFieldPermissionCheckResult,\n} from \"@datrix/core\";\nimport { isPermissionFn } from \"@datrix/core\";\nimport type { RequestContext } from \"./types\";\n\n/**\n * Build PermissionContext from RequestContext\n * Internal helper to satisfy PermissionFn signature\n */\nfunction buildPermCtx<T extends DatrixEntry>(\n\tctx: RequestContext,\n): PermissionContext<T> {\n\tconst permCtx: PermissionContext<T> = {\n\t\tuser: ctx.user ?? undefined,\n\t\tid: ctx.id,\n\t\taction: ctx.action,\n\t\tdatrix: ctx.datrix,\n\t};\n\n\t// Only add input if body exists (for exactOptionalPropertyTypes)\n\tif (ctx.body) {\n\t\t(permCtx as { input?: Record<string, unknown> }).input = ctx.body;\n\t}\n\n\treturn permCtx;\n}\n\n/**\n * Evaluate a single permission value\n *\n * @param value - Permission value to evaluate\n * @param ctx - Request context\n * @returns true if allowed, false otherwise\n */\nexport async function evaluatePermissionValue<TRoles extends string>(\n\tvalue: PermissionValue<TRoles> | undefined,\n\tctx: RequestContext,\n): Promise<boolean> {\n\t// Undefined means no restriction (allow)\n\tif (value === undefined) {\n\t\treturn true;\n\t}\n\n\t// Boolean: direct allow/deny\n\tif (typeof value === \"boolean\") {\n\t\treturn value;\n\t}\n\n\t// Function: evaluate with context\n\tif (isPermissionFn(value)) {\n\t\t// Create minimal context for permission function\n\t\tconst permCtx = buildPermCtx(ctx);\n\t\treturn await (value as PermissionFn)(permCtx);\n\t}\n\n\t// Array: check roles and/or functions (OR logic)\n\tif (Array.isArray(value)) {\n\t\t// No user means no role to check\n\t\tif (!ctx.user) {\n\t\t\t// But we still need to check if there are functions that might allow\n\t\t\tfor (const item of value) {\n\t\t\t\tif (isPermissionFn(item)) {\n\t\t\t\t\tconst permCtx = buildPermCtx(ctx);\n\t\t\t\t\tconst result = await (item as PermissionFn)(permCtx);\n\t\t\t\t\tif (result) return true;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\n\t\t// Check each item with OR logic\n\t\tfor (const item of value) {\n\t\t\tif (typeof item === \"string\") {\n\t\t\t\t// Role check\n\t\t\t\tif (ctx.user.role === item) {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t} else if (isPermissionFn(item)) {\n\t\t\t\t// Function check\n\t\t\t\tconst permCtx = buildPermCtx(ctx);\n\t\t\t\tconst result = await (item as PermissionFn)(permCtx);\n\t\t\t\tif (result) return true;\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\t// Unknown type, deny by default\n\treturn false;\n}\n\n/**\n * Check schema-level permission\n *\n * @param schema - Schema definition\n * @param ctx - Request context (contains action)\n * @param defaultPermission - Default permission if schema has no explicit permission\n * @returns Permission check result\n */\nexport async function checkSchemaPermission<TRoles extends string>(\n\tschema: SchemaDefinition<TRoles>,\n\tctx: RequestContext,\n\tdefaultPermission?: DefaultPermission<TRoles>,\n): Promise<PermissionCheckResult> {\n\tconst { action } = ctx;\n\n\t// Get permission value from schema or default\n\tconst schemaPermission = schema.permission as\n\t\t| SchemaPermission<TRoles>\n\t\t| undefined;\n\tlet permissionValue: PermissionValue<TRoles> | undefined;\n\n\tif (schemaPermission && schemaPermission[action] !== undefined) {\n\t\tpermissionValue = schemaPermission[action];\n\t} else if (defaultPermission && defaultPermission[action] !== undefined) {\n\t\tpermissionValue = defaultPermission[action];\n\t}\n\n\tconst allowed = await evaluatePermissionValue(permissionValue, ctx);\n\n\treturn {\n\t\tallowed,\n\t\treason: allowed\n\t\t\t? undefined\n\t\t\t: `Permission denied for ${action} on ${schema.name}`,\n\t};\n}\n\n/**\n * Check field-level read permissions and filter response\n *\n * @param schema - Schema definition\n * @param record - Record to filter\n * @param ctx - Request context\n * @returns Filtered record with denied fields removed\n */\nexport async function filterFieldsForRead<\n\tTRoles extends string,\n\tTRecord extends DatrixEntry,\n>(\n\tschema: SchemaDefinition<TRoles>,\n\trecord: TRecord,\n\tctx: RequestContext,\n): Promise<{ data: Partial<TRecord>; deniedFields: string[] }> {\n\tconst deniedFields: string[] = [];\n\tconst filtered: Partial<TRecord> = {};\n\n\tfor (const [fieldName, fieldValue] of Object.entries(record)) {\n\t\tconst fieldDef = schema.fields[fieldName];\n\n\t\t// If field not in schema, include it (system fields like id, createdAt)\n\t\tif (!fieldDef) {\n\t\t\t(filtered as Record<string, unknown>)[fieldName] = fieldValue;\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst fieldPermission = fieldDef.permission as\n\t\t\t| FieldPermission<TRoles>\n\t\t\t| undefined;\n\n\t\t// No permission defined = allow\n\t\tif (!fieldPermission || fieldPermission.read === undefined) {\n\t\t\t(filtered as Record<string, unknown>)[fieldName] = fieldValue;\n\t\t\tcontinue;\n\t\t}\n\n\t\t// Evaluate permission\n\t\tconst allowed = await evaluatePermissionValue(fieldPermission.read, ctx);\n\t\tif (allowed) {\n\t\t\t(filtered as Record<string, unknown>)[fieldName] = fieldValue;\n\t\t} else {\n\t\t\tdeniedFields.push(fieldName);\n\t\t}\n\t}\n\n\treturn { data: filtered, deniedFields };\n}\n\n/**\n * Check field-level write permissions\n *\n * @param schema - Schema definition\n * @param ctx - Request context (contains body as input)\n * @returns Result with denied fields (if any, should return 403)\n */\nexport async function checkFieldsForWrite<TRoles extends string>(\n\tschema: SchemaDefinition<TRoles>,\n\tctx: RequestContext,\n): Promise<FieldPermissionCheckResult> {\n\tconst deniedFields: string[] = [];\n\tconst input = ctx.body ?? {};\n\n\tfor (const fieldName of Object.keys(input)) {\n\t\tconst fieldDef = schema.fields[fieldName];\n\n\t\t// If field not in schema, skip (validator will handle)\n\t\tif (!fieldDef) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst fieldPermission = fieldDef.permission as\n\t\t\t| FieldPermission<TRoles>\n\t\t\t| undefined;\n\n\t\t// No permission defined = allow\n\t\tif (!fieldPermission || fieldPermission.write === undefined) {\n\t\t\tcontinue;\n\t\t}\n\n\t\t// Evaluate permission\n\t\tconst allowed = await evaluatePermissionValue(fieldPermission.write, ctx);\n\t\tif (!allowed) {\n\t\t\tdeniedFields.push(fieldName);\n\t\t}\n\t}\n\n\treturn {\n\t\tallowed: deniedFields.length === 0,\n\t\tdeniedFields: deniedFields.length > 0 ? deniedFields : undefined,\n\t};\n}\n\n/**\n * Map HTTP method to permission action\n */\nexport function methodToAction(method: string): PermissionAction {\n\tswitch (method.toUpperCase()) {\n\t\tcase \"GET\":\n\t\t\treturn \"read\";\n\t\tcase \"POST\":\n\t\t\treturn \"create\";\n\t\tcase \"PATCH\":\n\t\tcase \"PUT\":\n\t\t\treturn \"update\";\n\t\tcase \"DELETE\":\n\t\t\treturn \"delete\";\n\t\tdefault:\n\t\t\treturn \"read\";\n\t}\n}\n\n/**\n * Filter array of records for read permission (used for list endpoints)\n */\nexport async function filterRecordsForRead<\n\tTRoles extends string,\n\tTRecord extends DatrixEntry,\n>(\n\tschema: SchemaDefinition<TRoles>,\n\trecords: readonly TRecord[],\n\tctx: RequestContext,\n): Promise<Partial<TRecord>[]> {\n\tconst filtered: Partial<TRecord>[] = [];\n\n\tfor (const record of records) {\n\t\tconst { data } = await filterFieldsForRead(schema, record, ctx);\n\t\tfiltered.push(data);\n\t}\n\n\treturn filtered;\n}\n","/**\n * Main Query Parser\n *\n * Parses complete query strings into ParsedQuery.\n * Combines fields, where, populate, pagination, and sorting.\n */\n\nimport type { OrderByItem, OrderDirection } from \"@datrix/core\";\nimport {\n\tParserError,\n\tbuildErrorLocation,\n\ttype RawQueryParams,\n\ttype ParserOptions,\n\ttype ParsedPagination,\n\ttype ParsedSort,\n\ttype ParsedQuery,\n} from \"@datrix/core\";\nimport { validateFieldName } from \"@datrix/core\";\nimport { parseFields } from \"./fields-parser\";\nimport { parseWhere } from \"./where-parser\";\nimport { parsePopulate } from \"./populate-parser\";\nimport { paginationError, sortError } from \"./errors\";\nimport { DatrixEntry, DatrixRecord } from \"@datrix/core\";\n\n/**\n * Default parser options\n */\nconst DEFAULT_OPTIONS: Required<ParserOptions> = {\n\tmaxPageSize: 100,\n\tdefaultPageSize: 25,\n\tmaxPopulateDepth: 5,\n\tallowedOperators: [],\n\tstrictMode: false,\n};\n\n/**\n * Parse query parameters into ParsedQuery\n *\n * @param params - Raw query parameters\n * @param options - Parser options\n * @returns Result with ParsedQuery or ParserError\n */\nexport function parseQuery(\n\tparams: RawQueryParams,\n\toptions?: Partial<ParserOptions>,\n): ParsedQuery<DatrixEntry> {\n\tconst opts: Required<ParserOptions> = {\n\t\t...DEFAULT_OPTIONS,\n\t\t...options,\n\t};\n\n\tconst fields = parseFields(params);\n\tconst where = parseWhere(params);\n\tconst populate = parsePopulate(params, opts.maxPopulateDepth);\n\tconst pagination = parsePagination(params, opts);\n\tconst sort = parseSort(params);\n\n\tconst unknownParams = detectUnknownParams(params);\n\tif (unknownParams.length > 0) {\n\t\tthrow new ParserError(\n\t\t\t`Unknown query parameters: ${unknownParams.join(\", \")}`,\n\t\t\t{\n\t\t\t\tcode: \"UNKNOWN_PARAMETER\",\n\t\t\t\tparser: \"query\",\n\t\t\t\tlocation: buildErrorLocation(unknownParams),\n\t\t\t\treceived: unknownParams,\n\t\t\t\texpected:\n\t\t\t\t\t\"Known parameters: fields, where, populate, page, pageSize, sort\",\n\t\t\t\tsuggestion:\n\t\t\t\t\t\"Check for typos. Common mistake: use 'where' instead of 'filters'.\",\n\t\t\t},\n\t\t);\n\t}\n\n\tconst result = {\n\t\t...(fields !== undefined && fields !== \"*\" && { select: fields }),\n\t\t...(where !== undefined && { where }),\n\t\t...(populate !== undefined && { populate }),\n\t\t...(pagination !== undefined && {\n\t\t\tpage: pagination.page ?? 1,\n\t\t\tpageSize: pagination.pageSize ?? opts.defaultPageSize,\n\t\t}),\n\t\t...(sort !== undefined &&\n\t\t\tArray.isArray(sort) &&\n\t\t\tsort.length > 0 && { orderBy: sort }),\n\t} as ParsedQuery<DatrixEntry>;\n\n\treturn result;\n}\n\n/**\n * Parse pagination parameters\n * Throws ParserError on validation failure\n */\nfunction parsePagination(\n\tparams: RawQueryParams,\n\toptions: Required<ParserOptions>,\n): ParsedPagination | undefined {\n\tconst { page, pageSize } = params;\n\n\t// Parse page/pageSize with defaults\n\tconst parsedPage = page !== undefined ? parseInt(String(page), 10) : 1;\n\tconst parsedPageSize =\n\t\tpageSize !== undefined\n\t\t\t? parseInt(String(pageSize), 10)\n\t\t\t: options.defaultPageSize;\n\n\t// Maximum safe page number to prevent overflow\n\tconst MAX_PAGE_NUMBER = 1000000;\n\n\t// Validate page\n\tif (isNaN(parsedPage) || parsedPage < 1) {\n\t\tpaginationError.invalidPage(page ?? \"\", [\"page\"]);\n\t}\n\n\tif (parsedPage > MAX_PAGE_NUMBER) {\n\t\tpaginationError.maxPageNumberExceeded(parsedPage, MAX_PAGE_NUMBER, [\n\t\t\t\"page\",\n\t\t]);\n\t}\n\n\t// Validate pageSize\n\tif (isNaN(parsedPageSize) || parsedPageSize < 1) {\n\t\tpaginationError.invalidPageSize(pageSize ?? \"\", [\"pageSize\"]);\n\t}\n\n\tif (parsedPageSize > options.maxPageSize) {\n\t\tpaginationError.maxPageSizeExceeded(parsedPageSize, options.maxPageSize, [\n\t\t\t\"pageSize\",\n\t\t]);\n\t}\n\n\treturn {\n\t\tpage: parsedPage,\n\t\tpageSize: parsedPageSize,\n\t};\n}\n\n/**\n * Parse sort parameters\n * Throws ParserError on validation failure\n *\n * Examples:\n * ?sort=name -> orderBy: [{ field: 'name', direction: 'asc' }]\n * ?sort=-createdAt -> orderBy: [{ field: 'createdAt', direction: 'desc' }]\n * ?sort=name,-createdAt -> multiple sorts\n */\nfunction parseSort(params: RawQueryParams): ParsedSort | undefined {\n\tconst sortParam = params[\"sort\"];\n\n\tif (sortParam === undefined) {\n\t\treturn undefined;\n\t}\n\n\t// Handle empty or whitespace-only sort\n\tif (typeof sortParam === \"string\" && sortParam.trim() === \"\") {\n\t\tsortError.emptyValue([]);\n\t}\n\n\tconst sorts: OrderByItem<DatrixRecord>[] = [];\n\n\t// Handle comma-separated sorts\n\tconst sortStrings =\n\t\ttypeof sortParam === \"string\"\n\t\t\t? sortParam.split(\",\").map((s) => s.trim())\n\t\t\t: Array.isArray(sortParam)\n\t\t\t\t? sortParam.map((s) => String(s).trim())\n\t\t\t\t: [String(sortParam).trim()];\n\n\tfor (const sortStr of sortStrings) {\n\t\tif (!sortStr) {\n\t\t\tcontinue;\n\t\t}\n\n\t\t// Check for descending order (leading -)\n\t\tconst isDescending = sortStr.startsWith(\"-\");\n\t\tconst field = isDescending ? sortStr.slice(1) : sortStr;\n\n\t\tif (!field) {\n\t\t\tsortError.invalidFieldName(sortStr, [sortStr]);\n\t\t}\n\n\t\tconst validation = validateFieldName(field);\n\t\tif (!validation.valid) {\n\t\t\tsortError.invalidFieldName(sortStr, [sortStr], {\n\t\t\t\tfieldValidationReason: validation.reason,\n\t\t\t});\n\t\t}\n\n\t\tconst direction: OrderDirection = isDescending ? \"desc\" : \"asc\";\n\t\tsorts.push({ field, direction });\n\t}\n\n\treturn sorts.length > 0 ? sorts : undefined;\n}\n\n/**\n * Known query parameter prefixes\n *\n * Any key not matching these is considered unknown.\n * This catches typos like \"filters\" (should be \"where\"),\n * \"limit\" (should be \"pageSize\"), etc.\n */\nconst KNOWN_PARAM_PREFIXES = [\n\t\"fields\",\n\t\"where\",\n\t\"populate\",\n\t\"page\",\n\t\"pageSize\",\n\t\"sort\",\n] as const;\n\n/**\n * Detect unknown/unrecognized query parameters\n *\n * Returns list of parameter keys that don't match any known prefix.\n * This prevents silent failures where typos like \"filters\" are ignored.\n */\nfunction detectUnknownParams(params: RawQueryParams): string[] {\n\tconst unknownKeys: string[] = [];\n\n\tfor (const key of Object.keys(params)) {\n\t\tconst isKnown = KNOWN_PARAM_PREFIXES.some(\n\t\t\t(prefix) => key === prefix || key.startsWith(`${prefix}[`),\n\t\t);\n\n\t\tif (!isKnown) {\n\t\t\tunknownKeys.push(key);\n\t\t}\n\t}\n\n\treturn unknownKeys;\n}\n","/**\n * Fields Parser\n *\n * Parses fields query params into SelectClause.\n * Examples:\n * ?fields[0]=name&fields[1]=email\n * ?fields=name,email\n */\n\nimport type { RawQueryParams } from \"@datrix/core\";\nimport { MAX_ARRAY_INDEX, validateFieldName } from \"@datrix/core\";\nimport { fieldsError } from \"./errors\";\n\n/**\n * Parse fields parameter\n * Throws ParserError on validation failure\n *\n * @param params - Raw query parameters\n * @returns SelectClause (string[] | '*' | undefined)\n * @throws {ParserError} When validation fails\n */\nexport function parseFields(\n\tparams: RawQueryParams,\n): string[] | \"*\" | undefined {\n\t// Check for suspicious parameters (fields[extra], fields_injection, etc.)\n\tconst suspiciousParams = Object.keys(params).filter(\n\t\t(key) =>\n\t\t\tkey.startsWith(\"fields\") &&\n\t\t\tkey !== \"fields\" &&\n\t\t\t!key.match(/^fields\\[\\d+\\]$/), // Allow fields[0], fields[1], etc.\n\t);\n\n\tif (suspiciousParams.length > 0) {\n\t\tfieldsError.suspiciousParams(suspiciousParams, []);\n\t}\n\n\t// Handle array format: fields[0]=name&fields[2]=email (sparse arrays allowed)\n\tconst arrayFields = extractArrayFields(params);\n\tif (arrayFields.length > 0) {\n\t\treturn validateAndReturn(arrayFields);\n\t}\n\n\t// Check for fields parameter\n\tconst fieldsParam = params[\"fields\"];\n\n\tif (fieldsParam === undefined) {\n\t\t// No fields specified, return wildcard (will select all)\n\t\treturn \"*\";\n\t}\n\n\t// Handle wildcard\n\tif (fieldsParam === \"*\") {\n\t\treturn \"*\";\n\t}\n\n\t// Handle comma-separated format: fields=name,email\n\tif (typeof fieldsParam === \"string\") {\n\t\tconst fields = fieldsParam\n\t\t\t.split(\",\")\n\t\t\t.map((f) => f.trim())\n\t\t\t.filter(Boolean);\n\n\t\t// Reject if all fields are empty after trimming\n\t\tif (fields.length === 0) {\n\t\t\tfieldsError.emptyValue([]);\n\t\t}\n\n\t\treturn validateAndReturn(fields);\n\t}\n\n\t// Handle array (from frameworks that parse query strings into arrays)\n\tif (Array.isArray(fieldsParam)) {\n\t\tconst fields = fieldsParam.map((f) => String(f).trim()).filter(Boolean);\n\n\t\t// Reject if all fields are empty after trimming\n\t\tif (fields.length === 0) {\n\t\t\tfieldsError.emptyValue([]);\n\t\t}\n\n\t\treturn validateAndReturn(fields);\n\t}\n\n\t// Invalid format\n\tfieldsError.invalidFormat([]);\n\n\treturn undefined;\n}\n\n/**\n * Extract fields from array-style parameters\n * Handles sparse arrays: fields[0]=name&fields[2]=email (fields[1] can be missing)\n *\n * This allows UI checkboxes where users select specific fields,\n * resulting in non-sequential indices.\n */\nfunction extractArrayFields(params: RawQueryParams): string[] {\n\tconst fields: string[] = [];\n\n\t// Find all fields[N] parameters\n\tfor (const key in params) {\n\t\tconst match = key.match(/^fields\\[(\\d+)\\]$/);\n\t\tif (!match) continue;\n\n\t\tconst index = parseInt(match[1]!, 10);\n\n\t\t// Prevent DoS attacks with extremely large indices\n\t\tif (index >= MAX_ARRAY_INDEX) {\n\t\t\tcontinue; // Skip invalid indices\n\t\t}\n\n\t\tconst value = params[key];\n\t\tif (typeof value === \"string\") {\n\t\t\tfields.push(value.trim());\n\t\t} else if (Array.isArray(value)) {\n\t\t\t// Framework might parse duplicate params as array\n\t\t\tfields.push(...value.map((v) => String(v).trim()));\n\t\t}\n\t}\n\n\treturn fields;\n}\n\n/**\n * Validate field names and return result\n */\nfunction validateAndReturn(fields: readonly string[]): string[] | \"*\" {\n\tif (fields.length === 0) {\n\t\treturn \"*\";\n\t}\n\n\t// Validate field names (alphanumeric, underscores, dots for nested fields)\n\tconst invalidFieldsWithReasons: Array<{ field: string; reason: string }> = [];\n\n\tfor (const field of fields) {\n\t\tconst validation = validateFieldName(field);\n\t\tif (!validation.valid) {\n\t\t\tinvalidFieldsWithReasons.push({ field, reason: validation.reason });\n\t\t}\n\t}\n\n\tif (invalidFieldsWithReasons.length > 0) {\n\t\tconst invalidFields = invalidFieldsWithReasons.map((item) => item.field);\n\t\tconst reasons = invalidFieldsWithReasons.map((item) => item.reason);\n\n\t\tfieldsError.invalidFieldNames(invalidFields, [], {\n\t\t\tvalidationReasons: reasons,\n\t\t});\n\t}\n\n\treturn fields as string[];\n}\n","/**\n * Parser Error Helpers\n *\n * Centralized error creation for all parsers.\n * Provides clean, type-safe error handling with rich context.\n */\n\nimport {\n\tParserError,\n\tbuildErrorLocation,\n\ttype WhereErrorContext,\n\ttype PopulateErrorContext,\n\ttype FieldsErrorContext,\n\ttype PaginationErrorContext,\n\ttype SortErrorContext,\n} from \"@datrix/core\";\nimport {\n\tMAX_WHERE_VALUE_LENGTH,\n\tMAX_LOGICAL_NESTING_DEPTH,\n} from \"@datrix/core\";\n\n/**\n * Where Parser Errors\n */\nexport const whereError = {\n\tinvalidOperator(\n\t\toperator: string,\n\t\tpath: string[],\n\t\tcontext?: Partial<WhereErrorContext>,\n\t) {\n\t\tthrow new ParserError(`Invalid WHERE operator: ${operator}`, {\n\t\t\tcode: \"INVALID_OPERATOR\",\n\t\t\tparser: \"where\",\n\t\t\tlocation: buildErrorLocation([\"where\", ...path], {\n\t\t\t\tqueryParam: context?.operatorPath,\n\t\t\t}),\n\t\t\treceived: operator,\n\t\t\texpected:\n\t\t\t\t\"One of: $eq, $ne, $gt, $gte, $lt, $lte, $in, $nin, $contains, $startsWith, $endsWith, $like, $ilike, $null, $notNull, $and, $or, $not\",\n\t\t\tsuggestion:\n\t\t\t\t\"Use a valid WHERE operator. See documentation for full list.\",\n\t\t\tcontext: {\n\t\t\t\toperator,\n\t\t\t\t...context,\n\t\t\t},\n\t\t});\n\t},\n\n\tinvalidFieldName(\n\t\tfieldName: string,\n\t\tpath: string[],\n\t\tcontext?: Partial<WhereErrorContext>,\n\t) {\n\t\tconst reasonDetail = context?.fieldValidationReason\n\t\t\t? ` (Reason: ${context.fieldValidationReason})`\n\t\t\t: \"\";\n\n\t\tthrow new ParserError(\n\t\t\t`Invalid field name in WHERE clause: ${fieldName}${reasonDetail}`,\n\t\t\t{\n\t\t\t\tcode: \"INVALID_FIELD_NAME\",\n\t\t\t\tparser: \"where\",\n\t\t\t\tlocation: buildErrorLocation([\"where\", ...path]),\n\t\t\t\treceived: fieldName,\n\t\t\t\texpected:\n\t\t\t\t\t\"Field name must start with letter/underscore and contain only alphanumeric characters, underscores, and dots\",\n\t\t\t\tsuggestion:\n\t\t\t\t\t\"Use valid field names (e.g., 'name', 'user_id', 'profile.age')\",\n\t\t\t\tcontext: {\n\t\t\t\t\toperator: fieldName,\n\t\t\t\t\t...context,\n\t\t\t\t},\n\t\t\t},\n\t\t);\n\t},\n\n\tinvalidArrayIndex(\n\t\tindex: string,\n\t\toperator: string,\n\t\tpath: string[],\n\t\tcontext?: Partial<WhereErrorContext>,\n\t) {\n\t\tthrow new ParserError(\n\t\t\t`Array index [${index}] can only follow array operators ($or, $and, $not, $in, $nin), found after: ${context?.previousOperator || \"unknown\"}`,\n\t\t\t{\n\t\t\t\tcode: \"ARRAY_INDEX_ERROR\",\n\t\t\t\tparser: \"where\",\n\t\t\t\tlocation: buildErrorLocation([\"where\", ...path], {\n\t\t\t\t\tindex: parseInt(index, 10),\n\t\t\t\t\tqueryParam: context?.operatorPath,\n\t\t\t\t}),\n\t\t\t\treceived: index,\n\t\t\t\texpected: \"Array index after $or, $and, $not, $in, or $nin\",\n\t\t\t\tsuggestion: \"Array indices can only be used with array operators\",\n\t\t\t\tcontext: {\n\t\t\t\t\toperator,\n\t\t\t\t\tarrayIndex: parseInt(index, 10),\n\t\t\t\t\t...context,\n\t\t\t\t},\n\t\t\t},\n\t\t);\n\t},\n\n\tarrayIndexAtStart(index: string, _path: string[]): never {\n\t\tthrow new ParserError(\n\t\t\t\"Array index cannot appear at the beginning of WHERE clause\",\n\t\t\t{\n\t\t\t\tcode: \"ARRAY_INDEX_ERROR\",\n\t\t\t\tparser: \"where\",\n\t\t\t\tlocation: buildErrorLocation([\"where\"], {\n\t\t\t\t\tindex: parseInt(index, 10),\n\t\t\t\t}),\n\t\t\t\treceived: index,\n\t\t\t\texpected: \"Field name or operator before array index\",\n\t\t\t\tsuggestion:\n\t\t\t\t\t\"WHERE clause must start with a field name, not an array index\",\n\t\t\t\tcontext: {\n\t\t\t\t\tarrayIndex: parseInt(index, 10),\n\t\t\t\t},\n\t\t\t},\n\t\t);\n\t},\n\n\tinvalidArrayIndexFormat(index: string, operator: string, path: string[]) {\n\t\tthrow new ParserError(\n\t\t\t`Invalid array index in ${operator}: ${index} (must be non-negative integer)`,\n\t\t\t{\n\t\t\t\tcode: \"ARRAY_INDEX_ERROR\",\n\t\t\t\tparser: \"where\",\n\t\t\t\tlocation: buildErrorLocation([\"where\", ...path]),\n\t\t\t\treceived: index,\n\t\t\t\texpected: \"Non-negative integer (0, 1, 2, ...)\",\n\t\t\t\tsuggestion: \"Use valid array indices starting from 0\",\n\t\t\t\tcontext: {\n\t\t\t\t\toperator,\n\t\t\t\t\tarrayIndex: NaN,\n\t\t\t\t},\n\t\t\t},\n\t\t);\n\t},\n\n\tarrayIndexNotStartingFromZero(\n\t\tfirstIndex: number,\n\t\toperator: string,\n\t\tpath: string[],\n\t) {\n\t\tthrow new ParserError(\n\t\t\t`Array indices for ${operator} must start from 0, found: ${firstIndex}`,\n\t\t\t{\n\t\t\t\tcode: \"CONSECUTIVE_INDEX_ERROR\",\n\t\t\t\tparser: \"where\",\n\t\t\t\tlocation: buildErrorLocation([\"where\", ...path], {\n\t\t\t\t\tindex: firstIndex,\n\t\t\t\t}),\n\t\t\t\treceived: firstIndex,\n\t\t\t\texpected: \"Array indices starting from 0\",\n\t\t\t\tsuggestion: \"Start array indices at 0: use [0], [1], [2], etc.\",\n\t\t\t\tcontext: {\n\t\t\t\t\toperator,\n\t\t\t\t\tarrayIndex: firstIndex,\n\t\t\t\t},\n\t\t\t},\n\t\t);\n\t},\n\n\tarrayIndexNotConsecutive(\n\t\tmissingIndex: number,\n\t\toperator: string,\n\t\tpath: string[],\n\t\tfoundIndices?: number[],\n\t) {\n\t\tconst indicesStr = foundIndices\n\t\t\t? `. Found: [${foundIndices.join(\", \")}]`\n\t\t\t: \"\";\n\n\t\tthrow new ParserError(\n\t\t\t`Array indices for ${operator} must be consecutive. Missing index: ${missingIndex}${indicesStr}`,\n\t\t\t{\n\t\t\t\tcode: \"CONSECUTIVE_INDEX_ERROR\",\n\t\t\t\tparser: \"where\",\n\t\t\t\tlocation: buildErrorLocation([\"where\", ...path], {\n\t\t\t\t\tindex: missingIndex,\n\t\t\t\t}),\n\t\t\t\treceived: foundIndices\n\t\t\t\t\t? `Indices: [${foundIndices.join(\", \")}]`\n\t\t\t\t\t: `Gap at index ${missingIndex}`,\n\t\t\t\texpected: \"Consecutive indices: [0, 1, 2, ...]\",\n\t\t\t\tsuggestion: `Add ${operator}[${missingIndex}] to fix the gap`,\n\t\t\t\tcontext: {\n\t\t\t\t\toperator,\n\t\t\t\t\tmissingIndex,\n\t\t\t\t\tfoundIndices,\n\t\t\t\t},\n\t\t\t},\n\t\t);\n\t},\n\n\tmaxValueLength(actualLength: number, path: string[]) {\n\t\tthrow new ParserError(\n\t\t\t`WHERE value exceeds maximum length of ${MAX_WHERE_VALUE_LENGTH} characters`,\n\t\t\t{\n\t\t\t\tcode: \"MAX_LENGTH_EXCEEDED\",\n\t\t\t\tparser: \"where\",\n\t\t\t\tlocation: buildErrorLocation([\"where\", ...path]),\n\t\t\t\treceived: `${actualLength} characters`,\n\t\t\t\texpected: `Maximum ${MAX_WHERE_VALUE_LENGTH} characters`,\n\t\t\t\tsuggestion:\n\t\t\t\t\t\"Reduce the length of your query value or use a different approach\",\n\t\t\t\tcontext: {\n\t\t\t\t\toperator: \"value_length\",\n\t\t\t\t},\n\t\t\t},\n\t\t);\n\t},\n\n\tmaxDepthExceeded(depth: number, path: string[]) {\n\t\tconst pathStr = path.length > 0 ? ` at path: ${path.join(\".\")}` : \"\";\n\n\t\tthrow new ParserError(\n\t\t\t`WHERE clause nesting depth exceeds maximum of ${MAX_LOGICAL_NESTING_DEPTH}${pathStr}`,\n\t\t\t{\n\t\t\t\tcode: \"MAX_DEPTH_EXCEEDED\",\n\t\t\t\tparser: \"where\",\n\t\t\t\tlocation: buildErrorLocation([\"where\", ...path], {\n\t\t\t\t\tdepth,\n\t\t\t\t}),\n\t\t\t\treceived: `Depth: ${depth}${pathStr}`,\n\t\t\t\texpected: `Maximum depth: ${MAX_LOGICAL_NESTING_DEPTH}`,\n\t\t\t\tsuggestion: \"Simplify query structure or split into multiple requests\",\n\t\t\t\tcontext: {\n\t\t\t\t\toperator: \"nesting_depth\",\n\t\t\t\t\tcurrentPath: path.join(\".\"),\n\t\t\t\t\tdepth,\n\t\t\t\t},\n\t\t\t},\n\t\t);\n\t},\n\n\temptyLogicalOperator(operator: string, path: string[]) {\n\t\tthrow new ParserError(\n\t\t\t`Logical operator ${operator} requires at least one condition`,\n\t\t\t{\n\t\t\t\tcode: \"EMPTY_VALUE\",\n\t\t\t\tparser: \"where\",\n\t\t\t\tlocation: buildErrorLocation([\"where\", ...path]),\n\t\t\t\treceived: \"empty array\",\n\t\t\t\texpected: \"At least one condition\",\n\t\t\t\tsuggestion: `Add at least one condition to ${operator} operator`,\n\t\t\t\tcontext: {\n\t\t\t\t\toperator,\n\t\t\t\t},\n\t\t\t},\n\t\t);\n\t},\n\n\temptyArrayOperator(operator: string, path: string[]) {\n\t\tthrow new ParserError(`Operator ${operator} requires a non-empty array`, {\n\t\t\tcode: \"EMPTY_VALUE\",\n\t\t\tparser: \"where\",\n\t\t\tlocation: buildErrorLocation([\"where\", ...path]),\n\t\t\treceived: \"empty array\",\n\t\t\texpected: \"Non-empty array\",\n\t\t\tsuggestion: `Provide at least one value for ${operator} operator`,\n\t\t\tcontext: {\n\t\t\t\toperator,\n\t\t\t},\n\t\t});\n\t},\n\n\tinvalidOperatorValue(\n\t\toperator: string,\n\t\tvalueType: string,\n\t\tpath: string[],\n\t\treceivedValue?: unknown,\n\t) {\n\t\tconst valuePreview =\n\t\t\treceivedValue !== undefined\n\t\t\t\t? `: ${JSON.stringify(receivedValue).slice(0, 50)}`\n\t\t\t\t: \"\";\n\n\t\tthrow new ParserError(\n\t\t\t`Operator ${operator} requires array but received ${valueType}${valuePreview}`,\n\t\t\t{\n\t\t\t\tcode: \"INVALID_VALUE_TYPE\",\n\t\t\t\tparser: \"where\",\n\t\t\t\tlocation: buildErrorLocation([\"where\", ...path]),\n\t\t\t\treceived: `${valueType}${valuePreview}`,\n\t\t\t\texpected: \"array (e.g., [1, 2, 3])\",\n\t\t\t\tsuggestion: `Use array format: where[field][${operator}][0]=value1&where[field][${operator}][1]=value2`,\n\t\t\t\tcontext: {\n\t\t\t\t\toperator,\n\t\t\t\t\treceivedType: valueType,\n\t\t\t\t},\n\t\t\t},\n\t\t);\n\t},\n};\n\n/**\n * Populate Parser Errors\n */\nexport const populateError = {\n\tinvalidRelation(\n\t\trelation: string,\n\t\tpath: string[],\n\t\tcontext?: Partial<PopulateErrorContext>,\n\t) {\n\t\tthrow new ParserError(`Invalid relation name: ${relation}`, {\n\t\t\tcode: \"INVALID_FIELD_NAME\",\n\t\t\tparser: \"populate\",\n\t\t\tlocation: buildErrorLocation([\"populate\", ...path], {\n\t\t\t\tdepth: context?.currentDepth,\n\t\t\t}),\n\t\t\treceived: relation,\n\t\t\texpected:\n\t\t\t\t\"Relation name must start with letter/underscore and contain only alphanumeric characters and underscores\",\n\t\t\tsuggestion: \"Use valid relation names (e.g., 'author', 'user_profile')\",\n\t\t\tcontext: {\n\t\t\t\trelation,\n\t\t\t\t...context,\n\t\t\t},\n\t\t});\n\t},\n\n\tmaxDepthExceeded(\n\t\tdepth: number,\n\t\tmaxDepth: number,\n\t\tpath: string[],\n\t\tcontext?: Partial<PopulateErrorContext>,\n\t) {\n\t\tthrow new ParserError(\"Maximum populate depth exceeded\", {\n\t\t\tcode: \"MAX_DEPTH_EXCEEDED\",\n\t\t\tparser: \"populate\",\n\t\t\tlocation: buildErrorLocation([\"populate\", ...path], {\n\t\t\t\tdepth,\n\t\t\t}),\n\t\t\treceived: depth,\n\t\t\texpected: `Maximum depth: ${maxDepth}`,\n\t\t\tsuggestion:\n\t\t\t\t\"Reduce nesting level or increase maxPopulateDepth in parser options\",\n\t\t\tcontext: {\n\t\t\t\tcurrentDepth: depth,\n\t\t\t\tmaxDepth,\n\t\t\t\trelationPath: path.join(\".\"),\n\t\t\t\t...context,\n\t\t\t},\n\t\t});\n\t},\n\n\temptyValue(path: string[]) {\n\t\tthrow new ParserError(\"Populate value cannot be empty\", {\n\t\t\tcode: \"EMPTY_VALUE\",\n\t\t\tparser: \"populate\",\n\t\t\tlocation: buildErrorLocation([\"populate\", ...path]),\n\t\t\treceived: \"empty string\",\n\t\t\texpected: \"Relation name or wildcard (*)\",\n\t\t\tsuggestion: \"Provide a relation name or use * to populate all relations\",\n\t\t\tcontext: {},\n\t\t});\n\t},\n\n\tinvalidType(type: string, path: string[]) {\n\t\tthrow new ParserError(\"Populate value must be a string or array\", {\n\t\t\tcode: \"INVALID_VALUE_TYPE\",\n\t\t\tparser: \"populate\",\n\t\t\tlocation: buildErrorLocation([\"populate\", ...path]),\n\t\t\treceived: type,\n\t\t\texpected: \"string or array\",\n\t\t\tsuggestion: \"Use a string (e.g., 'author') or array format\",\n\t\t\tcontext: {},\n\t\t});\n\t},\n\n\tinvalidFieldName(\n\t\tfieldName: string,\n\t\tpath: string[],\n\t\tcontext?: Partial<PopulateErrorContext>,\n\t): never {\n\t\tconst reasonDetail = context?.fieldValidationReason\n\t\t\t? ` (Reason: ${context.fieldValidationReason})`\n\t\t\t: \"\";\n\n\t\tthrow new ParserError(\n\t\t\t`Invalid field name in populate: ${fieldName}${reasonDetail}`,\n\t\t\t{\n\t\t\t\tcode: \"INVALID_FIELD_NAME\",\n\t\t\t\tparser: \"populate\",\n\t\t\t\tlocation: buildErrorLocation([\"populate\", ...path]),\n\t\t\t\treceived: fieldName,\n\t\t\t\texpected:\n\t\t\t\t\t\"Field name must start with letter/underscore and contain only alphanumeric characters, underscores, and dots\",\n\t\t\t\tsuggestion:\n\t\t\t\t\t\"Use valid field names (e.g., 'name', 'user_id', 'profile.age')\",\n\t\t\t\tcontext: {\n\t\t\t\t\tfieldName,\n\t\t\t\t\t...context,\n\t\t\t\t},\n\t\t\t},\n\t\t);\n\t},\n};\n\n/**\n * Fields Parser Errors\n */\nexport const fieldsError = {\n\tinvalidFieldNames(\n\t\tinvalidFields: readonly string[],\n\t\tpath: string[],\n\t\tcontext?: Partial<FieldsErrorContext>,\n\t) {\n\t\tconst reasonDetail =\n\t\t\tcontext?.validationReasons && context.validationReasons.length > 0\n\t\t\t\t? ` (Reasons: ${context.validationReasons.join(\", \")})`\n\t\t\t\t: \"\";\n\n\t\tthrow new ParserError(\n\t\t\t`Invalid field names: ${invalidFields.join(\", \")}${reasonDetail}`,\n\t\t\t{\n\t\t\t\tcode: \"INVALID_FIELD_NAME\",\n\t\t\t\tparser: \"fields\",\n\t\t\t\tlocation: buildErrorLocation([\"fields\", ...path]),\n\t\t\t\treceived: invalidFields,\n\t\t\t\texpected:\n\t\t\t\t\t\"Field names must start with letter/underscore and contain only alphanumeric characters, underscores, and dots\",\n\t\t\t\tsuggestion:\n\t\t\t\t\t\"Use valid field names (e.g., 'name', 'user_id', 'profile.age')\",\n\t\t\t\tcontext: {\n\t\t\t\t\tinvalidFields: invalidFields as string[],\n\t\t\t\t\t...context,\n\t\t\t\t},\n\t\t\t},\n\t\t);\n\t},\n\n\temptyValue(path: string[]) {\n\t\tthrow new ParserError(\n\t\t\t\"Fields parameter is empty or contains only whitespace\",\n\t\t\t{\n\t\t\t\tcode: \"EMPTY_VALUE\",\n\t\t\t\tparser: \"fields\",\n\t\t\t\tlocation: buildErrorLocation([\"fields\", ...path]),\n\t\t\t\treceived: \"empty string\",\n\t\t\t\texpected: \"Field name(s) or wildcard (*)\",\n\t\t\t\tsuggestion:\n\t\t\t\t\t\"Provide field names (e.g., 'name,email') or use * for all fields\",\n\t\t\t\tcontext: {},\n\t\t\t},\n\t\t);\n\t},\n\n\tsuspiciousParams(params: readonly string[], path: string[]) {\n\t\tthrow new ParserError(`Unknown fields parameters: ${params.join(\", \")}`, {\n\t\t\tcode: \"UNKNOWN_PARAMETER\",\n\t\t\tparser: \"fields\",\n\t\t\tlocation: buildErrorLocation([\"fields\", ...path]),\n\t\t\treceived: params,\n\t\t\texpected: \"fields or fields[N] format\",\n\t\t\tsuggestion:\n\t\t\t\t\"Use 'fields=name,email' or 'fields[0]=name&fields[1]=email' format\",\n\t\t\tcontext: {\n\t\t\t\tsuspiciousParams: params as string[],\n\t\t\t},\n\t\t});\n\t},\n\n\tinvalidFormat(path: string[]) {\n\t\tthrow new ParserError(\"Invalid fields format\", {\n\t\t\tcode: \"INVALID_SYNTAX\",\n\t\t\tparser: \"fields\",\n\t\t\tlocation: buildErrorLocation([\"fields\", ...path]),\n\t\t\treceived: \"unknown format\",\n\t\t\texpected: \"string or array\",\n\t\t\tsuggestion: \"Use 'fields=name,email' or 'fields[0]=name' format\",\n\t\t\tcontext: {},\n\t\t});\n\t},\n};\n\n/**\n * Pagination Parser Errors\n */\nexport const paginationError = {\n\tinvalidLimit(\n\t\tvalue: string | number | readonly string[] | undefined,\n\t\tpath: string[],\n\t\tcontext?: Partial<PaginationErrorContext>,\n\t) {\n\t\tthrow new ParserError(`Invalid limit value: \"${value}\"`, {\n\t\t\tcode: \"INVALID_PAGINATION\",\n\t\t\tparser: \"pagination\",\n\t\t\tlocation: buildErrorLocation([\"pagination\", ...path]),\n\t\t\treceived: value,\n\t\t\texpected: \"Positive integer\",\n\t\t\tsuggestion: \"Provide a positive integer for limit (e.g., limit=10)\",\n\t\t\tcontext: {\n\t\t\t\tparameter: \"limit\",\n\t\t\t\t...context,\n\t\t\t},\n\t\t});\n\t},\n\n\tinvalidOffset(\n\t\tvalue: string | number | readonly string[] | undefined,\n\t\tpath: string[],\n\t\tcontext?: Partial<PaginationErrorContext>,\n\t) {\n\t\tthrow new ParserError(`Invalid offset value: \"${value}\"`, {\n\t\t\tcode: \"INVALID_PAGINATION\",\n\t\t\tparser: \"pagination\",\n\t\t\tlocation: buildErrorLocation([\"pagination\", ...path]),\n\t\t\treceived: value,\n\t\t\texpected: \"Non-negative integer\",\n\t\t\tsuggestion: \"Provide a non-negative integer for offset (e.g., offset=0)\",\n\t\t\tcontext: {\n\t\t\t\tparameter: \"offset\",\n\t\t\t\t...context,\n\t\t\t},\n\t\t});\n\t},\n\n\tinvalidPage(\n\t\tvalue: string | number | readonly string[],\n\t\tpath: string[],\n\t\tcontext?: Partial<PaginationErrorContext>,\n\t) {\n\t\tthrow new ParserError(`Invalid page value: \"${value}\" (must be >= 1)`, {\n\t\t\tcode: \"INVALID_PAGINATION\",\n\t\t\tparser: \"pagination\",\n\t\t\tlocation: buildErrorLocation([\"pagination\", ...path]),\n\t\t\treceived: value,\n\t\t\texpected: \"Integer >= 1\",\n\t\t\tsuggestion: \"Provide a positive integer for page (e.g., page=1)\",\n\t\t\tcontext: {\n\t\t\t\tparameter: \"page\",\n\t\t\t\tminValue: 1,\n\t\t\t\t...context,\n\t\t\t},\n\t\t});\n\t},\n\n\tinvalidPageSize(\n\t\tvalue: string | number | readonly string[] | undefined,\n\t\tpath: string[],\n\t\tcontext?: Partial<PaginationErrorContext>,\n\t) {\n\t\tthrow new ParserError(`Invalid pageSize value: \"${value}\" (must be >= 1)`, {\n\t\t\tcode: \"INVALID_PAGINATION\",\n\t\t\tparser: \"pagination\",\n\t\t\tlocation: buildErrorLocation([\"pagination\", ...path]),\n\t\t\treceived: value,\n\t\t\texpected: \"Integer >= 1\",\n\t\t\tsuggestion: \"Provide a positive integer for pageSize (e.g., pageSize=25)\",\n\t\t\tcontext: {\n\t\t\t\tparameter: \"pageSize\",\n\t\t\t\tminValue: 1,\n\t\t\t\t...context,\n\t\t\t},\n\t\t});\n\t},\n\n\tmaxPageSizeExceeded(value: number, max: number, path: string[]) {\n\t\tthrow new ParserError(`Page size exceeds maximum (${max})`, {\n\t\t\tcode: \"MAX_VALUE_VIOLATION\",\n\t\t\tparser: \"pagination\",\n\t\t\tlocation: buildErrorLocation([\"pagination\", ...path]),\n\t\t\treceived: value,\n\t\t\texpected: `Maximum: ${max}`,\n\t\t\tsuggestion: `Reduce pageSize to ${max} or less`,\n\t\t\tcontext: {\n\t\t\t\tparameter: \"pageSize\",\n\t\t\t\tmaxValue: max,\n\t\t\t},\n\t\t});\n\t},\n\n\tmaxLimitExceeded(value: number, max: number, path: string[]) {\n\t\tthrow new ParserError(`Limit exceeds maximum page size (${max})`, {\n\t\t\tcode: \"MAX_VALUE_VIOLATION\",\n\t\t\tparser: \"pagination\",\n\t\t\tlocation: buildErrorLocation([\"pagination\", ...path]),\n\t\t\treceived: value,\n\t\t\texpected: `Maximum: ${max}`,\n\t\t\tsuggestion: `Reduce limit to ${max} or less`,\n\t\t\tcontext: {\n\t\t\t\tparameter: \"limit\",\n\t\t\t\tmaxValue: max,\n\t\t\t},\n\t\t});\n\t},\n\n\tmaxPageNumberExceeded(value: number, max: number, path: string[]) {\n\t\tthrow new ParserError(`Page number exceeds maximum (${max})`, {\n\t\t\tcode: \"PAGE_OUT_OF_RANGE\",\n\t\t\tparser: \"pagination\",\n\t\t\tlocation: buildErrorLocation([\"pagination\", ...path]),\n\t\t\treceived: value,\n\t\t\texpected: `Maximum: ${max}`,\n\t\t\tsuggestion: `Use page number ${max} or less`,\n\t\t\tcontext: {\n\t\t\t\tparameter: \"page\",\n\t\t\t\tmaxValue: max,\n\t\t\t},\n\t\t});\n\t},\n};\n\n/**\n * Sort Parser Errors\n */\nexport const sortError = {\n\temptyValue(path: string[]) {\n\t\tthrow new ParserError(\"Sort value cannot be empty\", {\n\t\t\tcode: \"EMPTY_VALUE\",\n\t\t\tparser: \"sort\",\n\t\t\tlocation: buildErrorLocation([\"sort\", ...path]),\n\t\t\treceived: \"empty string\",\n\t\t\texpected: \"Field name(s) with optional direction\",\n\t\t\tsuggestion:\n\t\t\t\t\"Provide field names (e.g., 'name' or '-createdAt' for descending)\",\n\t\t\tcontext: {},\n\t\t});\n\t},\n\n\tinvalidFieldName(\n\t\tfield: string,\n\t\tpath: string[],\n\t\tcontext?: Partial<SortErrorContext>,\n\t) {\n\t\tconst reasonDetail = context?.fieldValidationReason\n\t\t\t? ` (Reason: ${context.fieldValidationReason})`\n\t\t\t: \"\";\n\n\t\tthrow new ParserError(`Invalid sort field: ${field}${reasonDetail}`, {\n\t\t\tcode: \"INVALID_FIELD_NAME\",\n\t\t\tparser: \"sort\",\n\t\t\tlocation: buildErrorLocation([\"sort\", ...path]),\n\t\t\treceived: field,\n\t\t\texpected:\n\t\t\t\t\"Field name must start with letter/underscore and contain only alphanumeric characters, underscores, and dots. Use '-' prefix for descending order.\",\n\t\t\tsuggestion:\n\t\t\t\t\"Use valid field names (e.g., 'name', '-createdAt', 'user.age')\",\n\t\t\tcontext: {\n\t\t\t\tsortField: field,\n\t\t\t\tparameter: \"sort\",\n\t\t\t\t...context,\n\t\t\t},\n\t\t});\n\t},\n};\n","/**\n * Where Parser\n *\n * Parses where query params into WhereClause.\n * Examples:\n * ?where[status]=active\n * ?where[price][$gt]=100\n * ?where[name][$contains]=john\n */\n\nimport type { FallbackWhereClause } from \"@datrix/core\";\nimport type { RawQueryParams } from \"@datrix/core\";\nimport {\n\tvalidateFieldName,\n\tisValidWhereOperator,\n\tisLogicalOperator,\n\tgetOperatorValueType,\n} from \"@datrix/core\";\nimport { whereError } from \"./errors\";\n\n/**\n * Parse where parameter\n * Throws ParserError on validation failure\n *\n * @param params - Raw query parameters\n * @returns WhereClause or undefined\n * @throws {ParserError} When validation fails\n */\nexport function parseWhere(\n\tparams: RawQueryParams,\n): FallbackWhereClause | undefined {\n\tconst whereClause: Record<string, unknown> = {};\n\n\t// Find all where[...] parameters\n\tfor (const [key, value] of Object.entries(params)) {\n\t\tif (!key.startsWith(\"where[\")) {\n\t\t\tcontinue;\n\t\t}\n\n\t\t// Extract path: where[a][b][c] -> [\"a\", \"b\", \"c\"]\n\t\tconst parts = key\n\t\t\t.slice(5)\n\t\t\t.split(\"]\")\n\t\t\t.filter((p) => p.startsWith(\"[\"))\n\t\t\t.map((p) => p.slice(1));\n\t\tif (parts.length === 0) continue;\n\n\t\t// Validate parts (field names and operators)\n\t\tfor (let i = 0; i < parts.length; i++) {\n\t\t\tconst part = parts[i]!;\n\n\t\t\t// Check if it's an operator (starts with $)\n\t\t\tif (part.startsWith(\"$\")) {\n\t\t\t\t// Validate operator\n\t\t\t\tif (!isValidWhereOperator(part)) {\n\t\t\t\t\twhereError.invalidOperator(part, parts.slice(0, i), {\n\t\t\t\t\t\toperatorPath: key,\n\t\t\t\t\t});\n\t\t\t\t}\n\n\t\t\t\t// Operators cannot be at the start (i=0) unless they are logical operators\n\t\t\t\tif (i === 0 && !isLogicalOperator(part)) {\n\t\t\t\t\twhereError.invalidFieldName(part, [], {\n\t\t\t\t\t\tfieldValidationReason: \"INVALID_FORMAT\",\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t} else if (/^\\d+$/.test(part)) {\n\t\t\t\t// It's a numeric index - validate context\n\t\t\t\t// Index can only appear after logical operators ($or, $and)\n\t\t\t\tif (i === 0) {\n\t\t\t\t\twhereError.arrayIndexAtStart(part, []);\n\t\t\t\t}\n\n\t\t\t\tconst previousPart = parts[i - 1]!;\n\t\t\t\tif (![\"$or\", \"$and\", \"$in\", \"$nin\"].includes(previousPart)) {\n\t\t\t\t\twhereError.invalidArrayIndex(part, previousPart, parts.slice(0, i), {\n\t\t\t\t\t\tpreviousOperator: previousPart,\n\t\t\t\t\t\toperatorPath: key,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// It's a field name - validate it\n\t\t\t\tconst validation = validateFieldName(part);\n\t\t\t\tif (!validation.valid) {\n\t\t\t\t\twhereError.invalidFieldName(part, parts.slice(0, i), {\n\t\t\t\t\t\tfieldValidationReason: validation.reason,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Build the nested structure\n\t\tlet current = whereClause;\n\t\tconst pathParts = [...parts];\n\t\tfor (let i = 0; i < pathParts.length; i++) {\n\t\t\tconst part = pathParts[i]!;\n\t\t\tconst isLast = i === pathParts.length - 1;\n\n\t\t\tif (isLast) {\n\t\t\t\t// Find operator context for proper value parsing\n\t\t\t\t// Only use operator context for STRING operators (not array operators like $in, $nin)\n\t\t\t\t// Array operators' elements should be parsed normally (as numbers, strings, etc.)\n\t\t\t\tlet operatorContext: string | undefined;\n\t\t\t\tconst isArrayIndex = /^\\d+$/.test(part);\n\n\t\t\t\tif (part.startsWith(\"$\")) {\n\t\t\t\t\t// Current part is the operator: where[field][$op]=value\n\t\t\t\t\tconst expectedType = getOperatorValueType(part);\n\t\t\t\t\t// Only set context for string operators (to prevent number coercion)\n\t\t\t\t\tif (expectedType === \"string\") {\n\t\t\t\t\t\toperatorContext = part;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t// Note: For array indices (e.g., $in[0]), we don't set operatorContext\n\t\t\t\t// because array elements should be parsed as their natural types\n\n\t\t\t\t// Parse the value with operator context\n\t\t\t\tconst parsedValue = parseValue(value, operatorContext);\n\n\t\t\t\t// Validate operator value type only when operator itself is the last part\n\t\t\t\t// (not for array indices like $in[0], $nin[1])\n\t\t\t\tif (part.startsWith(\"$\") && !isArrayIndex) {\n\t\t\t\t\tvalidateOperatorValue(part, parsedValue, pathParts);\n\t\t\t\t}\n\n\t\t\t\tcurrent[part] = parsedValue;\n\t\t\t} else {\n\t\t\t\tif (current[part] === undefined) {\n\t\t\t\t\tcurrent[part] = {};\n\t\t\t\t}\n\t\t\t\tcurrent = current[part] as Record<string, unknown>;\n\t\t\t}\n\t\t}\n\t}\n\n\t// Transform into Final WhereClause\n\tconst transformResult = transformToFinalWhere(whereClause);\n\tconst finalClause = transformResult as FallbackWhereClause;\n\n\t// If no where parameters found, return undefined\n\tif (Object.keys(finalClause).length === 0) {\n\t\treturn undefined;\n\t}\n\n\t// Validate nesting depth\n\tvalidateNestingDepth(finalClause);\n\n\treturn finalClause;\n}\n\n/**\n * Post-process the object to handle logical operators which should be arrays\n */\nfunction transformToFinalWhere(obj: unknown): unknown {\n\tif (obj === null || typeof obj !== \"object\" || Array.isArray(obj)) {\n\t\treturn obj;\n\t}\n\n\tconst typedObj = obj as Record<string, unknown>;\n\tconst result: Record<string, unknown> = {};\n\n\tfor (const [key, value] of Object.entries(typedObj)) {\n\t\t// Operators that require array transformation\n\t\t// NOTE: $not is NOT an array operator - it takes a single object, not an array\n\t\tconst arrayOperators = [\"$or\", \"$and\", \"$in\", \"$nin\"];\n\n\t\tif (arrayOperators.includes(key)) {\n\t\t\t// Transform object with numeric keys into array\n\t\t\tif (\n\t\t\t\ttypeof value === \"object\" &&\n\t\t\t\tvalue !== null &&\n\t\t\t\t!Array.isArray(value)\n\t\t\t) {\n\t\t\t\tconst valueObj = value as Record<string, unknown>;\n\t\t\t\tconst keys = Object.keys(valueObj);\n\n\t\t\t\t// Validate that all keys are numeric\n\t\t\t\tconst numericKeys: number[] = [];\n\t\t\t\tfor (const k of keys) {\n\t\t\t\t\tconst num = Number(k);\n\t\t\t\t\tif (isNaN(num) || !Number.isInteger(num) || num < 0) {\n\t\t\t\t\t\twhereError.invalidArrayIndexFormat(k, key, [key]);\n\t\t\t\t\t}\n\t\t\t\t\tnumericKeys.push(num);\n\t\t\t\t}\n\n\t\t\t\t// Sort and validate consecutive sequence starting from 0\n\t\t\t\tconst sortedKeys = numericKeys.sort((a, b) => a - b);\n\n\t\t\t\tif (sortedKeys.length > 0 && sortedKeys[0] !== 0) {\n\t\t\t\t\twhereError.arrayIndexNotStartingFromZero(sortedKeys[0]!, key, [key]);\n\t\t\t\t}\n\n\t\t\t\tfor (let i = 0; i < sortedKeys.length; i++) {\n\t\t\t\t\tif (sortedKeys[i] !== i) {\n\t\t\t\t\t\twhereError.arrayIndexNotConsecutive(i, key, [key], sortedKeys);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// For $in/$nin, values are primitives - don't recursively transform\n\t\t\t\t// For $or/$and, values are conditions - recursively transform\n\t\t\t\tif ([\"$in\", \"$nin\"].includes(key)) {\n\t\t\t\t\tresult[key] = sortedKeys.map((idx) => valueObj[String(idx)]);\n\t\t\t\t} else {\n\t\t\t\t\tconst transformed: unknown[] = [];\n\t\t\t\t\tfor (const idx of sortedKeys) {\n\t\t\t\t\t\tconst transformResult = transformToFinalWhere(\n\t\t\t\t\t\t\tvalueObj[String(idx)],\n\t\t\t\t\t\t);\n\t\t\t\t\t\ttransformed.push(transformResult);\n\t\t\t\t\t}\n\t\t\t\t\tresult[key] = transformed;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tconst transformResult = transformToFinalWhere(value);\n\t\t\t\tresult[key] = transformResult;\n\t\t\t}\n\t\t} else {\n\t\t\tconst transformResult = transformToFinalWhere(value);\n\t\t\tresult[key] = transformResult;\n\t\t}\n\t}\n\n\treturn result;\n}\n\n/**\n * Parse value from string/array\n * Handles: strings, numbers, booleans, null, arrays (for $in, $nin)\n *\n * @param value - The raw value to parse\n * @param operator - Optional operator context for type-aware parsing\n */\nfunction parseValue(\n\tvalue: string | readonly string[] | undefined,\n\toperator?: string,\n): unknown {\n\tif (value === undefined) {\n\t\treturn undefined;\n\t}\n\n\t// Handle array (for $in, $nin operators)\n\tif (Array.isArray(value)) {\n\t\tconst parsed: unknown[] = [];\n\t\tfor (const v of value) {\n\t\t\tif (typeof v === \"string\") {\n\t\t\t\tconst result = parseSingleValue(v, operator);\n\t\t\t\tparsed.push(result);\n\t\t\t} else {\n\t\t\t\tparsed.push(v);\n\t\t\t}\n\t\t}\n\t\treturn parsed;\n\t}\n\n\tif (typeof value === \"string\") {\n\t\treturn parseSingleValue(value, operator);\n\t}\n\n\treturn value;\n}\n\n/**\n * Parse a single value from string\n * Returns Result to handle validation errors\n *\n * @param value - The raw string value to parse\n * @param operator - Optional operator context for type-aware parsing\n */\nfunction parseSingleValue(value: string, operator?: string): unknown {\n\t// Import MAX_WHERE_VALUE_LENGTH\n\tconst MAX_WHERE_VALUE_LENGTH = 1000;\n\n\t// Check value length first - reject instead of truncate\n\tif (value.length > MAX_WHERE_VALUE_LENGTH) {\n\t\twhereError.maxValueLength(value.length, []);\n\t}\n\n\t// If operator expects string, return as-is (no type coercion)\n\tif (operator) {\n\t\tconst expectedType = getOperatorValueType(operator);\n\t\tif (expectedType === \"string\") {\n\t\t\treturn value;\n\t\t}\n\t}\n\n\t// Handle special values\n\tif (value === \"null\") {\n\t\treturn null;\n\t}\n\n\tif (value === \"true\") {\n\t\treturn true;\n\t}\n\n\tif (value === \"false\") {\n\t\treturn false;\n\t}\n\n\t/*\n\t// Try to parse as number\n\tconst num = Number(value);\n\tif (!isNaN(num) && value.trim() !== '') {\n\t\treturn num;\n\t}\n\t*/\n\n\t// Return as string\n\treturn value;\n}\n\n/**\n * Validate operator value type\n */\nfunction validateOperatorValue(\n\toperator: string,\n\tvalue: unknown,\n\tpath: string[],\n): void {\n\tconst expectedType = getOperatorValueType(operator);\n\n\tif (!expectedType) {\n\t\t// Unknown operator (shouldn't happen, already validated)\n\t\treturn;\n\t}\n\n\t// Check type-specific requirements\n\tif (expectedType === \"array\") {\n\t\tif (!Array.isArray(value)) {\n\t\t\twhereError.invalidOperatorValue(operator, typeof value, path, value);\n\t\t}\n\n\t\t// Check if array is empty\n\t\tif ((value as []).length === 0) {\n\t\t\twhereError.emptyArrayOperator(operator, path);\n\t\t}\n\t}\n}\n\n/**\n * Validate nesting depth for logical operators\n */\nfunction validateNestingDepth(\n\tclause: FallbackWhereClause,\n\tdepth: number = 0,\n\tpath: string[] = [],\n): void {\n\tconst MAX_LOGICAL_NESTING_DEPTH = 10;\n\n\tif (depth > MAX_LOGICAL_NESTING_DEPTH) {\n\t\twhereError.maxDepthExceeded(depth, path);\n\t}\n\n\t// Check nested logical operators\n\tfor (const [key, value] of Object.entries(clause)) {\n\t\tif (isLogicalOperator(key) && Array.isArray(value)) {\n\t\t\t// Validate that logical operators have array of conditions\n\t\t\tif (value.length === 0) {\n\t\t\t\twhereError.emptyLogicalOperator(key, [...path, key]);\n\t\t\t}\n\n\t\t\t// Recursively check each condition\n\t\t\tfor (const condition of value) {\n\t\t\t\tif (typeof condition === \"object\" && condition !== null) {\n\t\t\t\t\tvalidateNestingDepth(condition as FallbackWhereClause, depth + 1, [\n\t\t\t\t\t\t...path,\n\t\t\t\t\t\tkey,\n\t\t\t\t\t]);\n\t\t\t\t}\n\t\t\t}\n\t\t} else if (\n\t\t\ttypeof value === \"object\" &&\n\t\t\tvalue !== null &&\n\t\t\t!Array.isArray(value)\n\t\t) {\n\t\t\t// Recursively check nested objects\n\t\t\tvalidateNestingDepth(value as FallbackWhereClause, depth, [...path, key]);\n\t\t}\n\t}\n}\n","/**\n * Populate Parser\n *\n * Parses populate query params into PopulateClause.\n * Examples:\n * ?populate=* -> populate all relations\n * ?populate[profile]=* -> populate profile with all fields\n * ?populate[profile][fields][0]=name -> populate profile with specific fields\n * ?populate[posts][populate][comments]=* -> nested populate\n */\n\nimport type { RawQueryParams } from \"@datrix/core\";\nimport { DatrixRecord } from \"@datrix/core\";\nimport { PopulateClause, PopulateOptions } from \"@datrix/core\";\nimport { validateFieldName } from \"@datrix/core\";\nimport { populateError } from \"./errors\";\n\n/**\n * Default max populate depth\n */\nconst DEFAULT_MAX_DEPTH = 5;\n\n/**\n * Parse populate parameter\n * Throws ParserError on validation failure\n *\n * @param params - Raw query parameters\n * @param maxDepth - Maximum nesting depth (default: 5)\n * @returns PopulateClause or undefined\n * @throws {ParserError} When validation fails\n */\nexport function parsePopulate(\n\tparams: RawQueryParams,\n\tmaxDepth: number = DEFAULT_MAX_DEPTH,\n): PopulateClause<DatrixRecord> | undefined {\n\t// Validate maxDepth\n\tif (maxDepth <= 0) {\n\t\tpopulateError.maxDepthExceeded(maxDepth, maxDepth, [\"config\"], {\n\t\t\tmaxDepth,\n\t\t});\n\t}\n\n\t// Build populate clause\n\tconst populateClause:\n\t\t| Record<string, PopulateOptions<DatrixRecord> | \"*\">\n\t\t| true = {};\n\n\t// Check for simple populate parameter (string)\n\tconst mainPopulate = params[\"populate\"];\n\tif (mainPopulate !== undefined) {\n\t\tif (mainPopulate === \"*\") {\n\t\t\treturn \"*\";\n\t\t}\n\n\t\tif (mainPopulate === \"true\") {\n\t\t\treturn true;\n\t\t}\n\n\t\tif (typeof mainPopulate === \"string\") {\n\t\t\t// Handle empty or whitespace-only string\n\t\t\tconst trimmed = mainPopulate.trim();\n\t\t\tif (trimmed === \"\") {\n\t\t\t\tpopulateError.emptyValue([]);\n\t\t\t}\n\n\t\t\t// Single relation: populate=author\n\t\t\tconst validation = validateFieldName(trimmed);\n\t\t\tif (!validation.valid) {\n\t\t\t\tpopulateError.invalidRelation(trimmed, [trimmed], {\n\t\t\t\t\tfieldValidationReason: validation.reason,\n\t\t\t\t});\n\t\t\t}\n\t\t\tpopulateClause[trimmed] = \"*\";\n\t\t} else if (Array.isArray(mainPopulate)) {\n\t\t\t// Handle array: populate[]=author&populate[]=comments\n\t\t\tfor (const rel of mainPopulate) {\n\t\t\t\tif (rel && typeof rel === \"string\") {\n\t\t\t\t\tconst trimmed = rel.trim();\n\t\t\t\t\t// Validate relation name\n\t\t\t\t\tconst validation = validateFieldName(trimmed);\n\t\t\t\t\tif (!validation.valid) {\n\t\t\t\t\t\tpopulateError.invalidRelation(trimmed, [trimmed], {\n\t\t\t\t\t\t\tfieldValidationReason: validation.reason,\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t\tpopulateClause[trimmed] = \"*\";\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\t// Invalid type (number, object, etc.)\n\t\t\tpopulateError.invalidType(typeof mainPopulate, []);\n\t\t}\n\t}\n\n\t// Extract all populate parameters\n\tconst populateParams = extractPopulateParams(params);\n\n\t// Detect if this is an indexed array format: populate[0]=author&populate[1]=comments\n\tconst indexedArrayRelations: string[] = [];\n\tfor (const [key, value] of Object.entries(params)) {\n\t\tconst indexMatch = key.match(/^populate\\[(\\d+)\\]$/);\n\t\tif (indexMatch && typeof value === \"string\") {\n\t\t\tconst index = Number(indexMatch[1]);\n\t\t\tconst relationName = value.trim();\n\n\t\t\t// Validate relation name\n\t\t\tconst validation = validateFieldName(relationName);\n\t\t\tif (!validation.valid) {\n\t\t\t\tpopulateError.invalidRelation(relationName, [relationName], {\n\t\t\t\t\tfieldValidationReason: validation.reason,\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tindexedArrayRelations[index] = relationName;\n\t\t}\n\t}\n\n\t// If we found indexed array format, return as string[]\n\tif (indexedArrayRelations.length > 0) {\n\t\treturn indexedArrayRelations.filter(\n\t\t\tBoolean,\n\t\t) as unknown as PopulateClause<DatrixRecord>; // Remove empty slots\n\t}\n\n\t// Parse each relation (object format)\n\tfor (const [relation, relationParams] of Object.entries(populateParams)) {\n\t\tconst parseResult = parseRelation(relation, relationParams, 1, maxDepth);\n\t\tpopulateClause[relation] = parseResult;\n\t}\n\n\t// If no populate parameters found at all, return undefined\n\tif (Object.keys(populateClause).length === 0) {\n\t\treturn undefined;\n\t}\n\n\treturn populateClause;\n}\n\n/**\n * Relation parameters extracted from query\n */\ninterface RelationParams {\n\treadonly fields?: readonly string[];\n\treadonly populate?: Record<string, RelationParams>;\n\treadonly isWildcard?: boolean;\n}\n\n/**\n * Extract populate parameters grouped by relation\n */\nfunction extractPopulateParams(\n\tparams: RawQueryParams,\n): Record<string, RelationParams> {\n\tconst relations: Record<string, Record<string, unknown>> = {};\n\n\tfor (const [key, value] of Object.entries(params)) {\n\t\tif (!key.startsWith(\"populate[\")) {\n\t\t\tcontinue;\n\t\t}\n\n\t\t// Parse: populate[relation][...][...]\n\t\tconst parts = key.match(/populate\\[([^\\]]+)\\](.*)$/);\n\t\tif (!parts) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst relation = parts[1];\n\t\tconst rest = parts[2];\n\n\t\tif (!relation) {\n\t\t\tcontinue;\n\t\t}\n\n\t\t// Initialize relation if not exists\n\t\tif (relations[relation] === undefined) {\n\t\t\trelations[relation] = {};\n\t\t}\n\n\t\t// Check if this is a wildcard: populate[relation]=*\n\t\tif (rest === \"\" && value === \"*\") {\n\t\t\trelations[relation][\"isWildcard\"] = true;\n\t\t\tcontinue;\n\t\t}\n\n\t\t// Parse remaining path: [fields][0], [populate][comments], etc.\n\t\tif (rest !== undefined) {\n\t\t\tparseRelationPath(relations[relation], rest, value);\n\t\t}\n\t}\n\n\treturn relations;\n}\n\n/**\n * Parse the path within a relation\n * Examples:\n * [fields][0] -> add to fields array\n * [populate][comments] -> nested populate\n */\nfunction parseRelationPath(\n\trelationData: Record<string, unknown>,\n\tpath: string,\n\tvalue: string | readonly string[] | undefined,\n): void {\n\tif (path === \"\") {\n\t\treturn;\n\t}\n\n\t// Match [key][...rest]\n\tconst match = path.match(/^\\[([^\\]]+)\\](.*)$/);\n\tif (!match) {\n\t\treturn;\n\t}\n\n\tconst key = match[1];\n\tconst rest = match[2];\n\n\tif (key === \"fields\") {\n\t\t// Handle fields array\n\t\tif (relationData[\"fields\"] === undefined) {\n\t\t\trelationData[\"fields\"] = [];\n\t\t}\n\n\t\tconst fieldsArray = Array.isArray(relationData[\"fields\"])\n\t\t\t? relationData[\"fields\"]\n\t\t\t: [];\n\n\t\tif (rest === \"\") {\n\t\t\t// populate[relation][fields]=* or comma-separated\n\t\t\tif (value === \"*\") {\n\t\t\t\trelationData[\"fields\"] = \"*\";\n\t\t\t} else if (typeof value === \"string\") {\n\t\t\t\tconst fields = value.split(\",\").map((f) => f.trim());\n\t\t\t\t// Validate each field\n\t\t\t\tfor (const field of fields) {\n\t\t\t\t\tconst validation = validateFieldName(field);\n\t\t\t\t\tif (!validation.valid) {\n\t\t\t\t\t\tpopulateError.invalidFieldName(field, [\"fields\"], {\n\t\t\t\t\t\t\tfieldValidationReason: validation.reason,\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tfieldsArray.push(...fields);\n\t\t\t}\n\t\t} else if (rest !== undefined) {\n\t\t\t// populate[relation][fields][0]=name\n\t\t\tconst indexMatch = rest.match(/^\\[(\\d+)\\]$/);\n\t\t\tif (indexMatch && typeof value === \"string\") {\n\t\t\t\tconst field = value.trim();\n\t\t\t\t// Validate field name\n\t\t\t\tconst validation = validateFieldName(field);\n\t\t\t\tif (!validation.valid) {\n\t\t\t\t\tpopulateError.invalidFieldName(field, [\"fields\"], {\n\t\t\t\t\t\tfieldValidationReason: validation.reason,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\tfieldsArray.push(field);\n\t\t\t}\n\t\t}\n\t} else if (key === \"populate\") {\n\t\t// Handle nested populate\n\t\tif (relationData[\"populate\"] === undefined) {\n\t\t\trelationData[\"populate\"] = {};\n\t\t}\n\n\t\tconst populateObj =\n\t\t\ttypeof relationData[\"populate\"] === \"object\" &&\n\t\t\t!Array.isArray(relationData[\"populate\"])\n\t\t\t\t? (relationData[\"populate\"] as Record<string, Record<string, unknown>>)\n\t\t\t\t: {};\n\n\t\trelationData[\"populate\"] = populateObj;\n\n\t\t// Handle instructions for the current relation's populates\n\t\tif (rest === \"\") {\n\t\t\tif (value === \"*\") {\n\t\t\t\t// populate[relation][populate]=*\n\t\t\t\trelationData[\"isWildcard\"] = true;\n\t\t\t} else if (typeof value === \"string\") {\n\t\t\t\t// populate[relation][populate]=profile,comments\n\t\t\t\tconst relations = value\n\t\t\t\t\t.split(\",\")\n\t\t\t\t\t.map((r) => r.trim())\n\t\t\t\t\t.filter(Boolean);\n\t\t\t\tfor (const rel of relations) {\n\t\t\t\t\tif (rel === \"*\") {\n\t\t\t\t\t\trelationData[\"isWildcard\"] = true;\n\t\t\t\t\t} else if (populateObj[rel] === undefined) {\n\t\t\t\t\t\tpopulateObj[rel] = { isWildcard: true };\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t} else if (rest !== undefined) {\n\t\t\t// populate[relation][populate][nestedRelation]...\n\t\t\tconst nestedMatch = rest.match(/^\\[([^\\]]+)\\](.*)$/);\n\t\t\tif (nestedMatch) {\n\t\t\t\tconst nestedRelation = nestedMatch[1];\n\t\t\t\tconst nestedRest = nestedMatch[2];\n\n\t\t\t\tif (nestedRelation) {\n\t\t\t\t\tif (populateObj[nestedRelation] === undefined) {\n\t\t\t\t\t\tpopulateObj[nestedRelation] = {};\n\t\t\t\t\t}\n\n\t\t\t\t\tif (nestedRest === \"\" && value === \"*\") {\n\t\t\t\t\t\tpopulateObj[nestedRelation][\"isWildcard\"] = true;\n\t\t\t\t\t} else if (nestedRest !== undefined) {\n\t\t\t\t\t\tparseRelationPath(populateObj[nestedRelation], nestedRest, value);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\n/**\n * Parse a single relation into PopulateOptions\n */\nfunction parseRelation(\n\trelation: string,\n\tparams: RelationParams,\n\tcurrentDepth: number,\n\tmaxDepth: number,\n\tpath: string[] = [],\n): PopulateOptions<DatrixRecord> | \"*\" {\n\t// Validate relation name\n\tconst validation = validateFieldName(relation);\n\tif (!validation.valid) {\n\t\tpopulateError.invalidRelation(relation, [...path, relation], {\n\t\t\trelationPath: [...path, relation].join(\".\"),\n\t\t\tfieldValidationReason: validation.reason,\n\t\t});\n\t}\n\n\t// Check depth\n\tif (currentDepth > maxDepth) {\n\t\tpopulateError.maxDepthExceeded(\n\t\t\tcurrentDepth,\n\t\t\tmaxDepth,\n\t\t\t[...path, relation],\n\t\t\t{\n\t\t\t\trelation,\n\t\t\t\trelationPath: [...path, relation].join(\".\"),\n\t\t\t\tcurrentDepth,\n\t\t\t\tnestedRelations: [...path, relation],\n\t\t\t},\n\t\t);\n\t}\n\n\t// Handle wildcard\n\tif (params.isWildcard) {\n\t\treturn \"*\";\n\t}\n\n\tconst options: Record<string, unknown> = {};\n\n\t// Add fields if specified\n\tif (params.fields !== undefined) {\n\t\tif (typeof params.fields === \"string\" && params.fields === \"*\") {\n\t\t\toptions[\"select\"] = \"*\";\n\t\t} else if (Array.isArray(params.fields) && params.fields.length > 0) {\n\t\t\toptions[\"select\"] = params.fields;\n\t\t}\n\t}\n\n\t// Add nested populates\n\tif (params.populate !== undefined) {\n\t\tconst nestedPopulate: Record<string, PopulateOptions<DatrixRecord> | \"*\"> =\n\t\t\t{};\n\n\t\tfor (const [nestedRelation, nestedParams] of Object.entries(\n\t\t\tparams.populate,\n\t\t)) {\n\t\t\tnestedPopulate[nestedRelation] = parseRelation(\n\t\t\t\tnestedRelation,\n\t\t\t\tnestedParams,\n\t\t\t\tcurrentDepth + 1,\n\t\t\t\tmaxDepth,\n\t\t\t\t[...path, relation],\n\t\t\t);\n\t\t}\n\n\t\tif (Object.keys(nestedPopulate).length > 0) {\n\t\t\toptions[\"populate\"] = nestedPopulate;\n\t\t}\n\t}\n\n\t// If no options specified, return wildcard\n\tif (Object.keys(options).length === 0) {\n\t\treturn \"*\";\n\t}\n\n\treturn options as PopulateOptions<DatrixRecord>;\n}\n","/**\n * Context Builder Middleware\n *\n * Builds unified request context from raw request\n * This is the SINGLE PLACE where all request preprocessing happens\n */\n\nimport type {\n\tRequestContext,\n\tHttpMethod,\n\tContextBuilderOptions,\n} from \"./types\";\nimport type { Datrix } from \"@datrix/core\";\nimport { ParserError } from \"@datrix/core\";\nimport { methodToAction } from \"./permission\";\nimport { parseQuery } from \"../parser\";\nimport { FallbackInput } from \"@datrix/core\";\nimport { AuthUser, IApiPlugin } from \"@datrix/core\";\n\n/**\n * Extract table name from URL path\n * /api/users -> 'users'\n * /api/users/123 -> 'users'\n */\nfunction extractTableNameFromPath(\n\tpathname: string,\n\tprefix: string,\n): string | null {\n\tconst segments = pathname.split(\"/\").filter(Boolean);\n\tconst prefixSegments = prefix.split(\"/\").filter(Boolean);\n\tconst pathSegments = segments.slice(prefixSegments.length);\n\n\tif (pathSegments.length === 0) {\n\t\treturn null;\n\t}\n\n\treturn pathSegments[0] ?? null;\n}\n\n/**\n * Extract record ID from URL path\n * /api/user/123 -> '123'\n * /api/user -> null\n */\nfunction extractIdFromPath(pathname: string, prefix: string): number | null {\n\tconst segments = pathname.split(\"/\").filter(Boolean);\n\tconst prefixSegments = prefix.split(\"/\").filter(Boolean);\n\tconst pathSegments = segments.slice(prefixSegments.length);\n\n\tif (pathSegments.length < 2) {\n\t\treturn null;\n\t}\n\tconst val = parseInt(pathSegments[1]!, 10);\n\treturn isNaN(val) ? null : val;\n}\n\n/**\n * Parser error wrapper for context building\n */\nexport class ContextBuildError extends Error {\n\treadonly parserError: ParserError;\n\n\tconstructor(parserError: ParserError) {\n\t\tsuper(parserError.message);\n\t\tthis.name = \"ContextBuildError\";\n\t\tthis.parserError = parserError;\n\t}\n}\n\n/**\n * Build Request Context\n *\n * This is the CENTRALIZED place where:\n * 1. Schema resolution happens\n * 2. Authentication happens (only if enabled)\n * 3. URL parsing happens\n * 4. Query parsing happens\n * 5. Body parsing happens\n *\n * ALL requests go through this function ONCE\n *\n * @throws {ContextBuildError} When query parsing fails\n */\nexport async function buildRequestContext<TRole extends string = string>(\n\trequest: Request,\n\tdatrix: Datrix,\n\tapi: IApiPlugin<TRole>,\n\toptions: ContextBuilderOptions = {},\n): Promise<RequestContext<TRole>> {\n\tconst apiPrefix = options.apiPrefix ?? \"/api\";\n\tconst url = new URL(request.url);\n\tconst method = request.method as HttpMethod;\n\tconst authEnabled = api.isAuthEnabled();\n\n\t// 1. RESOLVE SCHEMA from URL\n\tconst tableName = extractTableNameFromPath(url.pathname, apiPrefix);\n\tconst modelName =\n\t\ttableName === \"upload\" && api.upload\n\t\t\t? api.upload.getModelName()\n\t\t\t: datrix.getSchemas().findModelByTableName(tableName);\n\tconst schema = modelName ? (datrix.getSchema(modelName) ?? null) : null;\n\n\t// 2. DERIVE ACTION from HTTP method\n\tconst action = methodToAction(method);\n\n\t// 3. EXTRACT ID from URL\n\tconst id = extractIdFromPath(url.pathname, apiPrefix);\n\n\t// 4. AUTHENTICATE (only if auth is enabled)\n\tlet user: AuthUser | null = null;\n\tif (authEnabled && api.authManager) {\n\t\tconst authResult = await api.authManager.authenticate(request);\n\t\tuser = authResult?.user ?? null;\n\t}\n\n\t// 5. PARSE QUERY (from query string - works for all HTTP methods)\n\tlet query = null;\n\tconst queryParams: Record<string, string | string[]> = {};\n\turl.searchParams.forEach((value, key) => {\n\t\tconst existing = queryParams[key];\n\t\tif (existing !== undefined) {\n\t\t\tif (Array.isArray(existing)) {\n\t\t\t\texisting.push(value);\n\t\t\t} else {\n\t\t\t\tqueryParams[key] = [existing, value];\n\t\t\t}\n\t\t} else {\n\t\t\tqueryParams[key] = value;\n\t\t}\n\t});\n\n\tif (Object.keys(queryParams).length > 0) {\n\t\tquery = parseQuery(queryParams);\n\t}\n\n\t// 6. PARSE BODY (for POST/PATCH/PUT requests)\n\tlet body = null;\n\tif ([\"POST\", \"PATCH\", \"PUT\"].includes(method)) {\n\t\ttry {\n\t\t\tconst contentType = request.headers.get(\"content-type\");\n\t\t\tif (contentType?.includes(\"application/json\")) {\n\t\t\t\tbody = (await request.json()) as FallbackInput;\n\t\t\t}\n\t\t} catch {\n\t\t\t// Invalid JSON, body stays null\n\t\t}\n\t}\n\n\t// 7. EXTRACT HEADERS\n\tconst headers: Record<string, string> = {};\n\trequest.headers.forEach((value, key) => {\n\t\theaders[key] = value;\n\t});\n\n\t// 8. BUILD UNIFIED CONTEXT\n\treturn {\n\t\tschema,\n\t\taction,\n\t\tid,\n\t\tmethod,\n\t\tquery,\n\t\tbody,\n\t\theaders,\n\t\turl,\n\t\trequest,\n\t\tuser,\n\t\tdatrix,\n\t\tapi,\n\t\tauthEnabled,\n\t};\n}\n","/**\n * Unified Request Handler\n *\n * SINGLE ENTRY POINT for all API requests\n * Handles authentication, permission checking, and routing\n */\n\nimport type { Datrix } from \"@datrix/core\";\nimport type {\n\tRequestContext,\n\tContextBuilderOptions,\n} from \"../middleware/types\";\nimport { buildRequestContext } from \"../middleware/context\";\nimport {\n\tcheckSchemaPermission,\n\tcheckFieldsForWrite,\n\tfilterFieldsForRead,\n\tfilterRecordsForRead,\n} from \"../middleware/permission\";\nimport { jsonResponse, datrixErrorResponse } from \"./utils\";\nimport { handlerError } from \"../errors/api-error\";\nimport { DatrixError, DatrixValidationError } from \"@datrix/core\";\nimport type { DatrixEntry } from \"@datrix/core\";\nimport { ResponseData } from \"@datrix/core\";\nimport { IApiPlugin } from \"@datrix/core\";\n\n/**\n * Handle GET request\n */\nasync function handleGet(ctx: RequestContext): Promise<Response> {\n\tconst { datrix, schema, authEnabled } = ctx;\n\n\tif (!schema) {\n\t\tthrow handlerError.schemaNotFound(ctx.url.pathname);\n\t}\n\n\tconst { upload } = ctx.api;\n\n\tif (ctx.id) {\n\t\tconst result = await datrix.findById(schema.name, ctx.id, {\n\t\t\tselect: ctx.query?.select,\n\t\t\tpopulate: ctx.query?.populate,\n\t\t});\n\n\t\tif (!result) {\n\t\t\tthrow handlerError.recordNotFound(schema.name, ctx.id);\n\t\t}\n\n\t\tif (authEnabled) {\n\t\t\tconst { data: filteredResult } = await filterFieldsForRead(\n\t\t\t\tschema,\n\t\t\t\tresult,\n\t\t\t\tctx,\n\t\t\t);\n\t\t\tconst data = upload\n\t\t\t\t? await upload.injectUrls(filteredResult)\n\t\t\t\t: filteredResult;\n\t\t\treturn jsonResponse({ data });\n\t\t}\n\n\t\tconst data = upload ? await upload.injectUrls(result) : result;\n\t\treturn jsonResponse({ data });\n\t} else {\n\t\tconst page = ctx.query?.page ?? 1;\n\t\tconst pageSize = ctx.query?.pageSize ?? 25;\n\t\tconst limit = pageSize;\n\t\tconst offset = (page - 1) * pageSize;\n\n\t\tconst result = await datrix.findMany(schema.name, {\n\t\t\twhere: ctx.query?.where,\n\t\t\tselect: ctx.query?.select,\n\t\t\tpopulate: ctx.query?.populate,\n\t\t\torderBy: ctx.query?.orderBy,\n\t\t\tlimit,\n\t\t\toffset,\n\t\t});\n\n\t\tconst total = await datrix.count(schema.name, ctx.query?.where);\n\t\tconst totalPages = Math.ceil(total / pageSize);\n\n\t\tif (authEnabled) {\n\t\t\tconst filteredResults = await filterRecordsForRead(schema, result, ctx);\n\t\t\tconst data = upload\n\t\t\t\t? await upload.injectUrls(filteredResults)\n\t\t\t\t: filteredResults;\n\n\t\t\tconst response: ResponseData = {\n\t\t\t\tdata,\n\t\t\t\tmeta: { total, page, pageSize, totalPages },\n\t\t\t};\n\n\t\t\treturn jsonResponse(response);\n\t\t}\n\n\t\tconst data = upload ? await upload.injectUrls(result) : result;\n\t\tconst response: ResponseData = {\n\t\t\tdata,\n\t\t\tmeta: { total, page, pageSize, totalPages },\n\t\t};\n\n\t\treturn jsonResponse(response);\n\t}\n}\n\n/**\n * Handle POST request\n */\nasync function handlePost(ctx: RequestContext): Promise<Response> {\n\tconst { datrix, schema, authEnabled, body, query } = ctx;\n\n\tif (!schema) {\n\t\tthrow handlerError.schemaNotFound(ctx.url.pathname);\n\t}\n\n\tif (!body || typeof body !== \"object\" || Array.isArray(body)) {\n\t\tthrow handlerError.invalidBody();\n\t}\n\n\tif (authEnabled) {\n\t\tconst fieldCheck = await checkFieldsForWrite(schema, ctx);\n\n\t\tif (!fieldCheck.allowed) {\n\t\t\tthrow handlerError.permissionDenied(\n\t\t\t\t`Permission denied for fields: ${fieldCheck.deniedFields?.join(\", \")}`,\n\t\t\t\t{ deniedFields: fieldCheck.deniedFields },\n\t\t\t);\n\t\t}\n\t}\n\n\tconst { upload } = ctx.api;\n\n\tconst result = await datrix.create(schema.name, body, {\n\t\tselect: query?.select,\n\t\tpopulate: query?.populate,\n\t});\n\n\tif (authEnabled) {\n\t\tconst { data: filteredResult } = await filterFieldsForRead(\n\t\t\tschema,\n\t\t\tresult as unknown as DatrixEntry,\n\t\t\tctx,\n\t\t);\n\t\tconst data = upload\n\t\t\t? await upload.injectUrls(filteredResult)\n\t\t\t: filteredResult;\n\t\treturn jsonResponse({ data }, 201);\n\t}\n\n\tconst data = upload ? await upload.injectUrls(result) : result;\n\treturn jsonResponse({ data }, 201);\n}\n\n/**\n * Handle PATCH/PUT request (update)\n */\nasync function handleUpdate(ctx: RequestContext): Promise<Response> {\n\tconst { datrix, schema, authEnabled, body, id } = ctx;\n\n\tif (!schema) {\n\t\tthrow handlerError.schemaNotFound(ctx.url.pathname);\n\t}\n\n\tif (!id) {\n\t\tthrow handlerError.missingId(\"update\");\n\t}\n\n\tif (!body || typeof body !== \"object\" || Array.isArray(body)) {\n\t\tthrow handlerError.invalidBody();\n\t}\n\n\tconst existingRecord = await datrix.findById(schema.name, id);\n\n\tif (!existingRecord) {\n\t\tthrow handlerError.recordNotFound(schema.name, id);\n\t}\n\n\tif (authEnabled) {\n\t\tconst fieldCheck = await checkFieldsForWrite(schema, ctx);\n\n\t\tif (!fieldCheck.allowed) {\n\t\t\tthrow handlerError.permissionDenied(\n\t\t\t\t`Permission denied for fields: ${fieldCheck.deniedFields?.join(\", \")}`,\n\t\t\t\t{ deniedFields: fieldCheck.deniedFields },\n\t\t\t);\n\t\t}\n\t}\n\n\tconst result = await datrix.update(schema.name, id, body, {\n\t\tselect: ctx.query?.select,\n\t\tpopulate: ctx.query?.populate,\n\t});\n\n\tif (!result) {\n\t\tthrow handlerError.recordNotFound(schema.name, id);\n\t}\n\n\tconst { upload } = ctx.api;\n\n\tif (authEnabled) {\n\t\tconst { data: filteredResult } = await filterFieldsForRead(\n\t\t\tschema,\n\t\t\tresult as unknown as DatrixEntry,\n\t\t\tctx,\n\t\t);\n\t\tconst data = upload\n\t\t\t? await upload.injectUrls(filteredResult)\n\t\t\t: filteredResult;\n\t\treturn jsonResponse({ data });\n\t}\n\n\tconst data = upload ? await upload.injectUrls(result) : result;\n\treturn jsonResponse({ data });\n}\n\n/**\n * Handle DELETE request\n */\nasync function handleDelete(ctx: RequestContext): Promise<Response> {\n\tconst { datrix, schema, id } = ctx;\n\n\tif (!schema) {\n\t\tthrow handlerError.schemaNotFound(ctx.url.pathname);\n\t}\n\n\tif (!id) {\n\t\tthrow handlerError.missingId(\"delete\");\n\t}\n\n\tconst deleted = await datrix.delete(schema.name, id);\n\n\tif (!deleted) {\n\t\tthrow handlerError.recordNotFound(schema.name, id);\n\t}\n\n\treturn jsonResponse({ data: { id, deleted: true } });\n}\n\n/**\n * Unified Request Handler\n *\n * Main entry point - handles all HTTP methods\n *\n * Flow:\n * 1. Build context (auth, parse URL, parse query/body, resolve schema) - ONCE\n * 2. Check schema-level permission (only if auth enabled) - ONCE\n * 3. Route to method handler (which handles field-level permissions if auth enabled)\n */\nexport async function handleCrudRequest<TRole extends string = string>(\n\trequest: Request,\n\tdatrix: Datrix,\n\tapi: IApiPlugin<TRole>,\n\toptions?: ContextBuilderOptions,\n): Promise<Response> {\n\ttry {\n\t\tconst ctx = await buildRequestContext(request, datrix, api, options);\n\n\t\tif (!ctx.schema) {\n\t\t\tthrow handlerError.modelNotSpecified();\n\t\t}\n\n\t\tif (api.excludeSchemas.includes(ctx.schema.name)) {\n\t\t\tthrow handlerError.schemaNotFound(ctx.url.pathname);\n\t\t}\n\n\t\tif (ctx.authEnabled) {\n\t\t\tconst permissionResult = await checkSchemaPermission(\n\t\t\t\tctx.schema,\n\t\t\t\tctx,\n\t\t\t\tapi.authDefaultPermission,\n\t\t\t);\n\n\t\t\tif (!permissionResult.allowed) {\n\t\t\t\tthrow ctx.user\n\t\t\t\t\t? handlerError.permissionDenied(\"Schema scope permission denied\")\n\t\t\t\t\t: handlerError.unauthorized();\n\t\t\t}\n\t\t}\n\n\t\tapi.setUser(ctx.user);\n\n\t\tswitch (ctx.method) {\n\t\t\tcase \"GET\":\n\t\t\t\treturn await handleGet(ctx);\n\t\t\tcase \"POST\":\n\t\t\t\treturn await handlePost(ctx);\n\t\t\tcase \"PATCH\":\n\t\t\tcase \"PUT\":\n\t\t\t\treturn await handleUpdate(ctx);\n\t\t\tcase \"DELETE\":\n\t\t\t\treturn await handleDelete(ctx);\n\t\t\tdefault: {\n\t\t\t\tthrow handlerError.methodNotAllowed(ctx.method);\n\t\t\t}\n\t\t}\n\t} catch (error) {\n\t\tif (\n\t\t\terror instanceof DatrixValidationError ||\n\t\t\terror instanceof DatrixError\n\t\t) {\n\t\t\treturn datrixErrorResponse(error);\n\t\t}\n\n\t\tconsole.error(\"Unified Handler Error:\", error);\n\t\tconst message =\n\t\t\terror instanceof Error ? error.message : \"Internal server error\";\n\t\treturn datrixErrorResponse(\n\t\t\thandlerError.internalError(\n\t\t\t\tmessage,\n\t\t\t\terror instanceof Error ? error : undefined,\n\t\t\t),\n\t\t);\n\t}\n}\n","/**\n * Datrix API Helper\n *\n * Provides helper functions for handling API requests in user code.\n * Works with any framework that uses the Web Request/Response API natively\n * (Next.js App Router, Hono, Remix, Cloudflare Workers, Bun, Deno).\n * For Node.js frameworks (Express, Fastify, Koa) use toWebRequest / sendWebResponse.\n */\n\nimport { ApiPlugin } from \"../api\";\nimport { Datrix } from \"@datrix/core\";\nimport { datrixErrorResponse } from \"../handler/utils\";\nimport { handlerError } from \"../errors/api-error\";\nimport { DatrixError } from \"@datrix/core\";\n\n// ─── Node.js bridge types ─────────────────────────────────────────────────────\n// Duck-typed — no express/fastify dependency required.\n\ninterface NodeIncomingRequest {\n\tmethod: string;\n\turl?: string;\n\theaders: Record<string, string | string[] | undefined>;\n\tbody?: unknown;\n\tsocket?: { encrypted?: boolean };\n}\n\ninterface NodeOutgoingResponse {\n\tstatusCode: number;\n\tsetHeader(key: string, value: string): unknown;\n\tend(body: string): void;\n}\n\n// ─── Node.js bridge ───────────────────────────────────────────────────────────\n\n/**\n * Convert a Node.js-style incoming request (Express, Fastify, Koa, raw http.IncomingMessage)\n * to a Web API Request.\n *\n * Call this before passing the request to handleRequest.\n *\n * @example\n * ```ts\n * app.all(\"*\", async (req, res) => {\n * const request = toWebRequest(req)\n * const response = await handleRequest(await datrix(), request)\n * await sendWebResponse(res, response)\n * })\n * ```\n */\nexport function toWebRequest(req: NodeIncomingRequest): Request {\n\tconst protocol = req.socket?.encrypted ? \"https\" : \"http\";\n\tconst host = (req.headers[\"host\"] as string | undefined) ?? \"localhost\";\n\tconst url = `${protocol}://${host}${req.url ?? \"/\"}`;\n\n\tconst headers = new Headers();\n\tfor (const [key, value] of Object.entries(req.headers)) {\n\t\tif (value === undefined) continue;\n\t\tif (Array.isArray(value)) {\n\t\t\tfor (const v of value) headers.append(key, v);\n\t\t} else {\n\t\t\theaders.set(key, value);\n\t\t}\n\t}\n\n\tconst hasBody = req.body !== undefined && req.body !== null;\n\tconst methodAllowsBody = ![\"GET\", \"HEAD\"].includes(req.method.toUpperCase());\n\n\tlet body: RequestInit[\"body\"] = undefined!;\n\tif (hasBody && methodAllowsBody) {\n\t\tif (req.body instanceof Uint8Array || Buffer.isBuffer(req.body)) {\n\t\t\tbody = req.body as Uint8Array;\n\t\t} else if (typeof req.body === \"string\") {\n\t\t\tbody = req.body;\n\t\t} else {\n\t\t\tbody = JSON.stringify(req.body);\n\t\t}\n\t}\n\n\treturn new Request(url, {\n\t\tmethod: req.method,\n\t\theaders,\n\t\tbody,\n\t});\n}\n\n/**\n * Write a Web API Response back into a Node.js-style outgoing response\n * (Express, Fastify, Koa, raw http.ServerResponse).\n *\n * @example\n * ```ts\n * app.all(\"*\", async (req, res) => {\n * const request = toWebRequest(req)\n * const response = await handleRequest(await datrix(), request)\n * await sendWebResponse(res, response)\n * })\n * ```\n */\nexport async function sendWebResponse(\n\tres: NodeOutgoingResponse,\n\tresponse: Response,\n): Promise<void> {\n\tres.statusCode = response.status;\n\tresponse.headers.forEach((value, key) => {\n\t\tres.setHeader(key, value);\n\t});\n\tconst body = await response.text();\n\tres.end(body);\n}\n\n/**\n * Handle Datrix API Request\n *\n * This is the ONLY function users need to call in their route handlers.\n *\n * Handles:\n * - Config validation (is api configured?)\n * - API enabled check (is api.enabled = true?)\n * - Error handling (unexpected errors)\n * - Request routing (auth/crud)\n *\n * @param datrix - Datrix instance (must have getConfig() method)\n * @param request - Web API Request\n * @returns Web API Response\n *\n * @example\n * ```ts\n * // Next.js App Router — Web Request/Response native, no bridge needed\n * import datrix from \"@/datrix.config\"\n * import { handleRequest } from \"@datrix/api\"\n *\n * async function handler(request: Request): Promise<Response> {\n * return handleRequest(await datrix(), request)\n * }\n *\n * export const GET = handler\n * export const POST = handler\n * export const PATCH = handler\n * export const PUT = handler\n * export const DELETE = handler\n * ```\n *\n * @example\n * ```ts\n * // Express — use toWebRequest / sendWebResponse bridge\n * import express from \"express\"\n * import datrix from \"./datrix.config\"\n * import { handleRequest, toWebRequest, sendWebResponse } from \"@datrix/api\"\n *\n * const app = express()\n * app.use(express.raw({ type: \"*\\/*\" }))\n *\n * app.all(\"*\", async (req, res) => {\n * const request = toWebRequest(req)\n * const response = await handleRequest(await datrix(), request)\n * await sendWebResponse(res, response)\n * })\n * ```\n */\nexport async function handleRequest(\n\tdatrix: Datrix,\n\trequest: Request,\n): Promise<Response> {\n\ttry {\n\t\t// 1. Check if API is configured\n\t\tconst api = datrix.getPlugin(\"api\");\n\n\t\tif (!api || !(api instanceof ApiPlugin)) {\n\t\t\tconst errRes = handlerError.internalError(\n\t\t\t\t'API is not configured in datrix.config.ts. Add \"api: new DatrixApi({ ... })\" to your configuration.',\n\t\t\t);\n\t\t\treturn datrixErrorResponse(errRes);\n\t\t}\n\n\t\t// 2. Check if API is disabled\n\t\tif (!api.isEnabled()) {\n\t\t\tconst errRes = handlerError.internalError(\n\t\t\t\t'API is disabled. Remove \"disabled: true\" from ApiPlugin configuration.',\n\t\t\t);\n\t\t\treturn datrixErrorResponse(errRes);\n\t\t}\n\n\t\t// 3. Handle request (all logic inside DatrixApi)\n\t\treturn await api.handleRequest(request, datrix);\n\t} catch (error) {\n\t\tif (error instanceof DatrixError) {\n\t\t\treturn datrixErrorResponse(error);\n\t\t}\n\n\t\t// 4. Catch unexpected errors (should rarely happen)\n\t\tconsole.error(\"[Datrix API] Unexpected error:\", error);\n\t\tconst message =\n\t\t\terror instanceof Error ? error.message : \"Internal server error\";\n\t\tconst errRes = handlerError.internalError(\n\t\t\tmessage,\n\t\t\terror instanceof Error ? error : undefined,\n\t\t);\n\t\treturn datrixErrorResponse(errRes);\n\t}\n}\n","/**\n * Auth Middleware\n *\n * Handles authentication from JWT token or session cookie\n */\n\nimport { AuthUser } from \"@datrix/core\";\nimport type { AuthManager } from \"../auth/manager\";\n\n/**\n * Authenticate request\n *\n * Extracts and verifies JWT token or session from request\n * Returns authenticated user or null\n */\nexport async function authenticate(\n\trequest: Request,\n\tauthManager?: AuthManager,\n): Promise<AuthUser | null> {\n\tif (!authManager) {\n\t\treturn null;\n\t}\n\n\t// Use auth manager's authenticate method\n\tconst authContext = await authManager.authenticate(request);\n\n\tif (!authContext || !authContext.user) {\n\t\treturn null;\n\t}\n\n\treturn authContext.user;\n}\n","/**\n * Query Serializer\n *\n * Converts ParsedQuery objects into RawQueryParams query strings.\n */\n\nimport { DatrixEntry, DatrixRecord } from \"@datrix/core\";\nimport { ParsedQuery, RawQueryParams } from \"@datrix/core\";\nimport {\n\tWhereClause,\n\tPopulateClause,\n\tPopulateOptions,\n\tQueryPrimitive,\n} from \"@datrix/core\";\n\nexport function queryToParams<T extends DatrixEntry = DatrixRecord>(\n\tquery: ParsedQuery<T> | undefined,\n): string {\n\tif (!query) return \"\";\n\n\tconst serialized = serializeQuery(query);\n\n\tconst parts: string[] = [];\n\tObject.entries(serialized).forEach(([key, value]) => {\n\t\tif (Array.isArray(value)) {\n\t\t\tvalue.forEach((v) => {\n\t\t\t\t// Only encode the value, keep [ ] in key for readability\n\t\t\t\tparts.push(`${key}=${encodeURIComponent(String(v))}`);\n\t\t\t});\n\t\t} else if (value !== undefined) {\n\t\t\t// Only encode the value, keep [ ] in key for readability\n\t\t\tparts.push(`${key}=${encodeURIComponent(String(value))}`);\n\t\t}\n\t});\n\n\treturn parts.join(\"&\");\n}\n\n/**\n * Serialize ParsedQuery into RawQueryParams\n *\n * @param query - The parsed query object to serialize\n * @returns Records of query parameters\n */\nexport function serializeQuery<T extends DatrixEntry = DatrixEntry>(\n\tquery: ParsedQuery<T>,\n): RawQueryParams {\n\tconst params: Record<string, string | string[]> = {};\n\n\tconst validKeys = [\n\t\t\"select\",\n\t\t\"where\",\n\t\t\"populate\",\n\t\t\"orderBy\",\n\t\t\"page\",\n\t\t\"pageSize\",\n\t];\n\tconst queryKeys = Object.keys(query);\n\tconst unknownKeys = queryKeys.filter((key) => !validKeys.includes(key));\n\n\tif (unknownKeys.length > 0) {\n\t\tthrow new Error(\n\t\t\t`Unknown query keys: ${unknownKeys.join(\", \")}. Valid keys are: ${validKeys.join(\", \")}`,\n\t\t);\n\t}\n\n\t// 1. Fields (select)\n\tif (query.select) {\n\t\tif (query.select === \"*\") {\n\t\t\tparams[\"fields\"] = \"*\";\n\t\t} else if (Array.isArray(query.select)) {\n\t\t\tparams[\"fields\"] = query.select.join(\",\");\n\t\t}\n\t}\n\n\t// 2. Where\n\tif (query.where) {\n\t\tserializeWhere(query.where, \"where\", params);\n\t}\n\n\t// 3. Populate\n\tif (query.populate) {\n\t\tserializePopulate(query.populate, \"populate\", params);\n\t}\n\n\t// 4. OrderBy (sort)\n\tif (query.orderBy) {\n\t\tif (typeof query.orderBy === \"string\") {\n\t\t\t// Simple string format: 'name' or '-name'\n\t\t\tparams[\"sort\"] = query.orderBy;\n\t\t} else if (Array.isArray(query.orderBy)) {\n\t\t\t// Array format: ['name', '-createdAt'] or [{ field: 'name', direction: 'asc' }]\n\t\t\tconst sortStrings = query.orderBy.map((item) => {\n\t\t\t\tif (typeof item === \"string\") {\n\t\t\t\t\t// String item: 'name' or '-name'\n\t\t\t\t\treturn item;\n\t\t\t\t} else {\n\t\t\t\t\t// Object item: { field: 'name', direction: 'asc' }\n\t\t\t\t\treturn item.direction === \"desc\" ? `-${item.field}` : item.field;\n\t\t\t\t}\n\t\t\t});\n\t\t\tif (sortStrings.length > 0) {\n\t\t\t\tparams[\"sort\"] = sortStrings.join(\",\");\n\t\t\t}\n\t\t}\n\t}\n\n\t// 5. Pagination (page/pageSize only)\n\tif (query.page !== undefined) params[\"page\"] = String(query.page);\n\tif (query.pageSize !== undefined) params[\"pageSize\"] = String(query.pageSize);\n\n\treturn params;\n}\n\n/**\n * Recursive helper to serialize where clause\n */\nfunction serializeWhere<T extends DatrixEntry>(\n\twhere: WhereClause<T> | QueryPrimitive | readonly WhereClause<T>[],\n\tprefix: string,\n\tparams: Record<string, string | string[]>,\n) {\n\tif (where === null || typeof where !== \"object\") {\n\t\tparams[prefix] = String(where);\n\t\treturn;\n\t}\n\n\tfor (const [key, value] of Object.entries(where)) {\n\t\tconst newPrefix = `${prefix}[${key}]`;\n\n\t\tif (Array.isArray(value)) {\n\t\t\t// Handle logical operators like $or, $and, $not which take arrays of conditions\n\t\t\tif ([\"$or\", \"$and\", \"$not\"].includes(key)) {\n\t\t\t\tvalue.forEach((item, index) => {\n\t\t\t\t\tserializeWhere(item, `${newPrefix}[${index}]`, params);\n\t\t\t\t});\n\t\t\t} else {\n\t\t\t\t// Handle $in, $nin which take arrays of values\n\t\t\t\tvalue.forEach((item, index) => {\n\t\t\t\t\tparams[`${newPrefix}[${index}]`] = String(item);\n\t\t\t\t});\n\t\t\t}\n\t\t} else if (value !== null && typeof value === \"object\") {\n\t\t\tserializeWhere(value, newPrefix, params);\n\t\t} else {\n\t\t\tparams[newPrefix] = String(value);\n\t\t}\n\t}\n}\n\n/**\n * Recursive helper to serialize populate clause\n */\nfunction serializePopulate<T extends DatrixEntry>(\n\tpopulate: PopulateClause<T> | \"*\",\n\tprefix: string,\n\tparams: Record<string, string | string[] | true>,\n) {\n\tif (populate === \"*\") {\n\t\tparams[prefix] = \"*\";\n\t\treturn;\n\t}\n\n\tif (populate === \"true\") {\n\t\tparams[prefix] = true;\n\t\treturn;\n\t}\n\n\tif (populate === true) {\n\t\tparams[prefix] = true;\n\t\treturn;\n\t}\n\n\t// Handle string[] format: ['relation1', 'relation2']\n\t// Serialize as indexed array (preserves array format)\n\tif (Array.isArray(populate)) {\n\t\tpopulate.forEach((relation: string, index: number) => {\n\t\t\tparams[`${prefix}[${index}]`] = String(relation);\n\t\t});\n\t\treturn;\n\t}\n\n\tif (typeof populate !== \"object\") return;\n\n\tfor (const [relation, options] of Object.entries(populate)) {\n\t\tconst relPrefix = `${prefix}[${relation}]`;\n\n\t\tif (options === \"*\" || options === true) {\n\t\t\tparams[relPrefix] = options;\n\t\t} else if (typeof options === \"object\") {\n\t\t\tconst opts = options as PopulateOptions<T>;\n\n\t\t\t// select fields in populate\n\t\t\tif (opts.select) {\n\t\t\t\tif (opts.select === \"*\") {\n\t\t\t\t\tparams[`${relPrefix}[fields]`] = \"*\";\n\t\t\t\t} else if (Array.isArray(opts.select)) {\n\t\t\t\t\topts.select.forEach((field: string, index: number) => {\n\t\t\t\t\t\tparams[`${relPrefix}[fields][${index}]`] = field;\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// nested populate\n\t\t\tif (opts.populate) {\n\t\t\t\tserializePopulate(opts.populate, `${relPrefix}[populate]`, params);\n\t\t\t}\n\t\t}\n\t}\n}\n"],"mappings":"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","names":["import_core","import_node_crypto","import_core","import_node_crypto","import_core","import_core","import_core","import_core","import_core","import_core","import_core","import_core","import_core","import_core","import_core","MAX_WHERE_VALUE_LENGTH","MAX_LOGICAL_NESTING_DEPTH","import_core","import_core","data","response","import_core"]}