@objectstack/plugin-auth 6.7.0 → 6.7.1

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/auth-plugin.ts","../src/objectql-adapter.ts","../src/auth-schema-config.ts","../src/auth-manager.ts","../src/manifest.ts"],"sourcesContent":["// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * @objectstack/plugin-auth\n * \n * Authentication & Identity Plugin for ObjectStack\n * Powered by better-auth for robust, secure authentication\n * Uses ObjectQL for data persistence (no third-party ORM required)\n */\n\nexport * from './auth-plugin.js';\nexport * from './auth-manager.js';\nexport * from './objectql-adapter.js';\nexport * from './auth-schema-config.js';\nexport type { AuthConfig, AuthProviderConfig, AuthPluginConfig } from '@objectstack/spec/system';\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport { Plugin, PluginContext, IHttpServer } from '@objectstack/core';\nimport type { BetterAuthOptions } from 'better-auth';\nimport { AuthConfig } from '@objectstack/spec/system';\nimport {\n SETUP_APP,\n SystemOverviewDashboard,\n SetupAppTranslations,\n} from '@objectstack/platform-objects/apps';\nimport { AuthManager } from './auth-manager.js';\nimport {\n authIdentityObjects,\n authPluginManifestHeader,\n} from './manifest.js';\n\n/**\n * Auth Plugin Options\n * Extends AuthConfig from spec with additional runtime options\n */\nexport interface AuthPluginOptions extends Partial<AuthConfig> {\n /**\n * Whether to automatically register auth routes\n * @default true\n */\n registerRoutes?: boolean;\n \n /**\n * Base path for auth routes\n * @default '/api/v1/auth'\n */\n basePath?: string;\n\n /**\n * Override the datasource that owns the identity tables (sys_user,\n * sys_session, …) when AuthPlugin's manifest is registered.\n *\n * Defaults to `'cloud'` (control-plane DB) so the historical\n * single-tenant control-plane behaviour is preserved. Per-project\n * kernels in objectos pass `'default'` so identity tables live in the\n * project's own database — each project owns its own users.\n */\n manifestDatasource?: string;\n\n /**\n * Application-specific organization roles to register with Better-Auth's\n * organization plugin so invitations to those roles aren't rejected with\n * ROLE_NOT_FOUND. Forwarded as-is to AuthManager. See\n * {@link AuthManagerOptions.additionalOrgRoles} for details.\n */\n additionalOrgRoles?: string[];\n\n /**\n * Pass-through to better-auth's `databaseHooks` option. Used by\n * platform consumers (objectos kernel) to attach a\n * `user.create.after` hook that auto-provisions a personal\n * organization for JIT-created SSO users — better-auth's adapter\n * bypasses kernel-level ObjectQL middleware, so this is the only\n * hook point that fires for every user creation path (email signup,\n * social/OIDC sign-in, admin-created accounts).\n */\n databaseHooks?: BetterAuthOptions['databaseHooks'];\n}\n\n/**\n * Authentication Plugin\n * \n * Provides authentication and identity services for ObjectStack applications.\n * \n * **Dual-Mode Operation:**\n * - **Server mode** (HonoServerPlugin active): Registers HTTP routes at basePath,\n * forwarding all auth requests to better-auth's universal handler.\n * - **MSW/Mock mode** (no HTTP server): Gracefully skips route registration but\n * still registers the `auth` service, allowing HttpDispatcher.handleAuth() to\n * simulate auth flows (sign-up, sign-in, etc.) for development and testing.\n * \n * Features:\n * - Session management\n * - User registration/login\n * - OAuth providers (Google, GitHub, etc.)\n * - Organization/team support\n * - 2FA, passkeys, magic links\n * \n * This plugin registers:\n * - `auth` service (auth manager instance) — always\n * - `app.com.objectstack.system` service (system object definitions) — always\n * - HTTP routes for authentication endpoints — only when HTTP server is available\n * \n * Integrates with better-auth library to provide comprehensive\n * authentication capabilities including email/password, OAuth, 2FA,\n * magic links, passkeys, and organization support.\n */\nexport class AuthPlugin implements Plugin {\n name = 'com.objectstack.auth';\n type = 'standard';\n version = '1.0.0';\n dependencies: string[] = ['com.objectstack.engine.objectql']; // manifest service required\n \n private options: AuthPluginOptions;\n private authManager: AuthManager | null = null;\n\n constructor(options: AuthPluginOptions = {}) {\n this.options = {\n registerRoutes: true,\n basePath: '/api/v1/auth',\n ...options\n };\n }\n\n async init(ctx: PluginContext): Promise<void> {\n ctx.logger.info('Initializing Auth Plugin...');\n\n // Validate required configuration\n if (!this.options.secret) {\n throw new Error('AuthPlugin: secret is required');\n }\n\n // Get data engine service for database operations\n const dataEngine = ctx.getService<any>('data');\n if (!dataEngine) {\n ctx.logger.warn('No data engine service found - auth will use in-memory storage');\n }\n\n // Initialize auth manager with data engine\n this.authManager = new AuthManager({\n ...this.options,\n dataEngine,\n });\n\n // Register auth service\n ctx.registerService('auth', this.authManager);\n\n ctx.getService<{ register(m: any): void }>('manifest').register({\n ...authPluginManifestHeader,\n ...(this.options.manifestDatasource\n ? { defaultDatasource: this.options.manifestDatasource }\n : {}),\n objects: authIdentityObjects,\n // The platform Setup App is a static metadata artifact (lives in\n // @objectstack/platform-objects/apps). plugin-auth is the natural\n // owner of its registration since it loads first among the trio\n // (auth + security + audit) that supplies the underlying objects.\n apps: [SETUP_APP],\n // List views for each Setup-nav object are defined on the schema\n // itself via the canonical `listViews` map (e.g.\n // sys_user.listViews.{all_users,unverified,two_factor}). Registering\n // top-level views here is the legacy pre-M10.30c pattern — it caused\n // duplicate \"Users\"/\"Roles\"/\"Sessions\" tabs to appear alongside the\n // schema-derived ones, sometimes referencing nonexistent fields\n // (e.g. legacy `users.view` had phone/status/active columns that do\n // not exist on sys_user). Schema-embedded listViews is the single\n // source of truth.\n dashboards: [SystemOverviewDashboard],\n });\n\n ctx.logger.info('Auth Plugin initialized successfully');\n }\n\n async start(ctx: PluginContext): Promise<void> {\n ctx.logger.info('Starting Auth Plugin...');\n\n if (!this.authManager) {\n throw new Error('Auth manager not initialized');\n }\n\n // Contribute the static Setup App translation bundle into the kernel's\n // i18n service. Done in `kernel:ready` (not `init`) because the i18n\n // service plugin is typically registered AFTER capability-loaded service\n // plugins. Mirrors the pattern used by SettingsServicePlugin for its\n // built-in settings translations. Gracefully skips when no i18n service\n // is registered (e.g. lean test/MSW kernels) — Setup App labels then\n // fall back to the inline literals on the App / Dashboard metadata.\n ctx.hook('kernel:ready', async () => {\n try {\n const i18n = ctx.getService<{\n loadTranslations: (locale: string, data: Record<string, unknown>) => void;\n }>('i18n');\n let loaded = 0;\n for (const [locale, data] of Object.entries(SetupAppTranslations)) {\n if (data && typeof data === 'object') {\n try {\n i18n.loadTranslations(locale, data as Record<string, unknown>);\n loaded++;\n } catch (err: any) {\n ctx.logger.warn(\n `Auth: failed to load Setup App translations for '${locale}': ${err?.message ?? err}`,\n );\n }\n }\n }\n if (loaded > 0) {\n ctx.logger.info(\n `Auth: contributed Setup App translations (${loaded} locale${loaded > 1 ? 's' : ''})`,\n );\n }\n } catch {\n // i18n service not registered — Setup App falls back to inline labels.\n }\n });\n\n // Defer HTTP route registration to kernel:ready hook.\n // This ensures all plugins (including HonoServerPlugin) have completed\n // their init and start phases before we attempt to look up the\n // http-server service — making AuthPlugin resilient to plugin\n // loading order.\n if (this.options.registerRoutes) {\n ctx.hook('kernel:ready', async () => {\n // Inject the email service if available so better-auth callbacks\n // (sendResetPassword / sendVerificationEmail / sendInvitationEmail\n // / sendMagicLink) can actually deliver mail. Resolved here on\n // kernel:ready so EmailServicePlugin has had a chance to register.\n if (this.authManager) {\n try {\n const emailSvc = ctx.getService<any>('email');\n if (emailSvc) {\n this.authManager.setEmailService(emailSvc);\n ctx.logger.info('Auth: email service wired (transactional mail enabled)');\n }\n } catch {\n ctx.logger.info('Auth: no email service registered — auth callbacks will log instead of sending');\n }\n }\n\n let httpServer: IHttpServer | null = null;\n try {\n httpServer = ctx.getService<IHttpServer>('http-server');\n } catch {\n // Service not found — expected in MSW/mock mode\n }\n\n if (httpServer) {\n // Auto-detect the actual server URL when no explicit baseUrl was\n // configured, or when the configured baseUrl uses a different port\n // than the running server (e.g. port 3000 configured but 3002 bound).\n // getPort() is optional on IHttpServer; duck-type check for it.\n const serverWithPort = httpServer as IHttpServer & { getPort?: () => number };\n if (this.authManager && typeof serverWithPort.getPort === 'function') {\n const actualPort = serverWithPort.getPort();\n if (actualPort) {\n const configuredUrl = this.options.baseUrl || 'http://localhost:3000';\n const configuredOrigin = new URL(configuredUrl).origin;\n const actualUrl = `http://localhost:${actualPort}`;\n\n // Only auto-correct the port when the configured URL is already a\n // localhost URL (development mode). In production (Vercel/cloud) the\n // configured baseUrl is the real public hostname — never overwrite it\n // with a localhost URL, which would break OAuth callback URLs.\n const configuredIsLocalhost = configuredOrigin.startsWith('http://localhost');\n if (configuredIsLocalhost && configuredOrigin !== actualUrl) {\n this.authManager.setRuntimeBaseUrl(actualUrl);\n ctx.logger.info(\n `Auth baseUrl auto-updated to ${actualUrl} (configured: ${configuredUrl})`,\n );\n }\n }\n }\n\n // Route registration errors should propagate (server misconfiguration)\n this.registerAuthRoutes(httpServer, ctx);\n ctx.logger.info(`Auth routes registered at ${this.options.basePath}`);\n } else {\n ctx.logger.warn(\n 'No HTTP server available — auth routes not registered. ' +\n 'Auth service is still available for MSW/mock environments via HttpDispatcher.'\n );\n }\n });\n }\n\n // Register auth middleware on ObjectQL engine (if available)\n try {\n const ql = ctx.getService<any>('objectql');\n if (ql && typeof ql.registerMiddleware === 'function') {\n ql.registerMiddleware(async (opCtx: any, next: () => Promise<void>) => {\n // If context already has userId or isSystem, skip auth resolution\n if (opCtx.context?.userId || opCtx.context?.isSystem) {\n return next();\n }\n // Future: resolve session from AsyncLocalStorage or request context\n await next();\n });\n ctx.logger.info('Auth middleware registered on ObjectQL engine');\n }\n } catch (_e) {\n ctx.logger.debug('ObjectQL engine not available, skipping auth middleware registration');\n }\n\n ctx.logger.info('Auth Plugin started successfully');\n }\n\n async destroy(): Promise<void> {\n // Cleanup if needed\n this.authManager = null;\n }\n\n /**\n * Register authentication routes with HTTP server\n * \n * Uses better-auth's universal handler for all authentication requests.\n * This forwards all requests under basePath to better-auth, which handles:\n * - Email/password authentication\n * - OAuth providers (Google, GitHub, etc.)\n * - Session management\n * - Password reset\n * - Email verification\n * - 2FA, passkeys, magic links (if enabled)\n */\n private registerAuthRoutes(httpServer: IHttpServer, ctx: PluginContext): void {\n if (!this.authManager) return;\n\n const basePath = this.options.basePath || '/api/v1/auth';\n\n // Get raw Hono app to use native wildcard routing\n // Type assertion is safe here because we explicitly require Hono server as a dependency\n if (!('getRawApp' in httpServer) || typeof (httpServer as any).getRawApp !== 'function') {\n ctx.logger.error('HTTP server does not support getRawApp() - wildcard routing requires Hono server');\n throw new Error(\n 'AuthPlugin requires HonoServerPlugin for wildcard routing support. ' +\n 'Please ensure HonoServerPlugin is loaded before AuthPlugin.'\n );\n }\n\n const rawApp = (httpServer as any).getRawApp();\n\n // Register /config before the wildcard so it takes precedence.\n // better-auth has no /config endpoint, so without this explicit route\n // the wildcard below forwards the request and better-auth returns 404.\n rawApp.get(`${basePath}/config`, (c: any) => {\n try {\n const config = this.authManager!.getPublicConfig();\n return c.json({ success: true, data: config });\n } catch (error) {\n const err = error instanceof Error ? error : new Error(String(error));\n return c.json({ success: false, error: { code: 'auth_config_error', message: err.message } }, 500);\n }\n });\n\n // Bootstrap status — does an owner exist yet? Used by the Account SPA's\n // root route to decide between rendering /login (normal flow) and\n // /setup (first-run owner creation). Public, unauthenticated; only\n // returns a boolean so it can be polled before the user has any\n // credentials.\n rawApp.get(`${basePath}/bootstrap-status`, async (c: any) => {\n try {\n const dataEngine = this.authManager!.getDataEngine();\n if (!dataEngine) {\n // No data engine wired (e.g. MSW/mock mode) — assume bootstrapped\n // so the SPA falls through to its normal login flow.\n return c.json({ hasOwner: true });\n }\n const count = await dataEngine.count('sys_user', {});\n return c.json({ hasOwner: (count ?? 0) > 0 });\n } catch (error) {\n ctx.logger.warn('[AuthPlugin] bootstrap-status check failed; assuming bootstrapped', error as Error);\n return c.json({ hasOwner: true });\n }\n });\n\n // Device Authorization Grant (RFC 8628) endpoints — `/device/code`,\n // `/device/token`, `/device/approve`, `/device/deny`, `/device` — are\n // provided by better-auth's `device-authorization` plugin and reach\n // the wildcard handler below. Enable via\n // `AuthPluginConfig.deviceAuthorization`.\n\n // Set an INITIAL local password for users who signed in via SSO and\n // have no `credential` account yet. This is the \"Set local password\"\n // affordance on a per-environment kernel — it lets a user that the\n // platform onboarded via the objectstack-cloud OAuth provider sign in\n // with email/password against this environment going forward without\n // needing the SSO round-trip. Requires a valid session (so we know\n // which user is asking) and refuses if a credential already exists\n // (the user should use better-auth's /change-password endpoint in\n // that case so the current password is verified).\n rawApp.post(`${basePath}/set-initial-password`, async (c: any) => {\n try {\n let body: any = {};\n try { body = await c.req.json(); } catch { body = {}; }\n const newPassword: unknown = body?.newPassword;\n if (typeof newPassword !== 'string' || newPassword.length === 0) {\n return c.json({ success: false, error: { code: 'invalid_request', message: 'newPassword is required' } }, 400);\n }\n\n const authApi = await this.authManager!.getApi();\n const session = await authApi.getSession({ headers: c.req.raw.headers });\n if (!session?.user?.id) {\n return c.json({ success: false, error: { code: 'unauthorized', message: 'Sign in first' } }, 401);\n }\n const userId = session.user.id;\n\n const authCtx: any = await this.authManager!.getAuthContext();\n if (!authCtx?.internalAdapter || !authCtx?.password) {\n return c.json({ success: false, error: { code: 'unavailable', message: 'Auth context unavailable' } }, 503);\n }\n\n // Length checks mirror better-auth's emailAndPassword.{min,max}PasswordLength\n // so the validation surface is consistent across set / change / reset.\n const minLen = authCtx.password?.config?.minPasswordLength ?? 8;\n const maxLen = authCtx.password?.config?.maxPasswordLength ?? 128;\n if (newPassword.length < minLen) {\n return c.json({ success: false, error: { code: 'password_too_short', message: `Password must be at least ${minLen} characters` } }, 400);\n }\n if (newPassword.length > maxLen) {\n return c.json({ success: false, error: { code: 'password_too_long', message: `Password must be at most ${maxLen} characters` } }, 400);\n }\n\n const accounts = await authCtx.internalAdapter.findAccounts(userId);\n const existingCredential = accounts?.find?.((a: any) => a.providerId === 'credential' && a.password);\n if (existingCredential) {\n // Use /change-password (requires currentPassword) instead.\n return c.json({\n success: false,\n error: {\n code: 'credential_account_exists',\n message: 'A local password is already set for this account. Use change-password instead.',\n },\n }, 409);\n }\n\n const passwordHash = await authCtx.password.hash(newPassword);\n await authCtx.internalAdapter.createAccount({\n userId,\n providerId: 'credential',\n accountId: userId,\n password: passwordHash,\n });\n\n return c.json({ success: true });\n } catch (error) {\n const err = error instanceof Error ? error : new Error(String(error));\n ctx.logger.error('[AuthPlugin] set-initial-password failed', err);\n return c.json({ success: false, error: { code: 'internal', message: err.message } }, 500);\n }\n });\n\n // ────────────────────────────────────────────────────────────────────\n // OAuth admin: toggle the `disabled` flag on a registered OAuth client.\n //\n // Why this lives here (and not as a plain data-layer UPDATE on\n // sys_oauth_application): better-auth 1.6.11's stock admin update\n // endpoint (`/admin/oauth2/update-client`) does NOT accept `disabled`\n // in its Zod body schema, so the field gets silently stripped before\n // it reaches `updateClientEndpoint`. The column exists, the runtime\n // honours it everywhere (introspect, token, authorize, public-client\n // lookup), but no client-facing API can flip it.\n //\n // We close the gap by writing through better-auth's own adapter under\n // the `/api/v1/auth/*` namespace so all OAuth-application mutations\n // remain auth-routed (no generic data-layer bypass for the `oauth_client`\n // model). When upstream adds `disabled` to `adminUpdateOAuthClient`'s\n // schema, this route can be deleted and the sys_oauth_application\n // action retargeted to the stock endpoint.\n //\n // Upstream tracking: https://github.com/better-auth/better-auth\n rawApp.post(`${basePath}/admin/oauth2/toggle-disabled`, async (c: any) => {\n try {\n let body: any = {};\n try { body = await c.req.json(); } catch { body = {}; }\n const clientId: unknown = body?.client_id;\n const disabled: unknown = body?.disabled;\n if (typeof clientId !== 'string' || clientId.length === 0) {\n return c.json({ success: false, error: { code: 'invalid_request', message: 'client_id is required' } }, 400);\n }\n if (typeof disabled !== 'boolean') {\n return c.json({ success: false, error: { code: 'invalid_request', message: 'disabled must be a boolean' } }, 400);\n }\n\n const authApi = await this.authManager!.getApi();\n const session = await authApi.getSession({ headers: c.req.raw.headers });\n if (!session?.user?.id) {\n return c.json({ success: false, error: { code: 'unauthorized', message: 'Sign in first' } }, 401);\n }\n // The customSession plugin synthesizes `user.role = 'admin'` for\n // platform admins (admin_full_access permission set) and active-org\n // owners/admins; anyone else is denied.\n if ((session.user as any).role !== 'admin') {\n return c.json({ success: false, error: { code: 'forbidden', message: 'Admin role required' } }, 403);\n }\n\n // Write through the same ObjectQL data engine that better-auth's\n // adapter uses. We target the snake_case table name (`sys_oauth_application`,\n // mapped from better-auth's internal `oauthClient` model via\n // `auth-schema-config.ts`) because `$context.adapter`'s model-lookup\n // helper does not see plugin-provided model names from outside\n // better-auth's own endpoint invocation context. This is the same\n // physical row the better-auth runtime reads at introspect / token\n // / authorize time, so the toggle is fully honoured.\n const dataEngine: any = this.authManager!.getDataEngine();\n if (!dataEngine) {\n return c.json({ success: false, error: { code: 'unavailable', message: 'Data engine unavailable' } }, 503);\n }\n\n const existing = await dataEngine.findOne('sys_oauth_application', {\n where: { client_id: clientId },\n });\n if (!existing) {\n return c.json({ success: false, error: { code: 'not_found', message: 'OAuth client not found' } }, 404);\n }\n\n const updated = await dataEngine.update('sys_oauth_application', {\n id: existing.id,\n disabled,\n updated_at: new Date(Math.floor(Date.now() / 1000) * 1000),\n });\n if (!updated) {\n return c.json({ success: false, error: { code: 'internal', message: 'Unable to update OAuth client' } }, 500);\n }\n\n return c.json({\n success: true,\n data: {\n client_id: clientId,\n disabled,\n },\n });\n } catch (error) {\n const err = error instanceof Error ? error : new Error(String(error));\n ctx.logger.error('[AuthPlugin] toggle-disabled failed', err);\n return c.json({ success: false, error: { code: 'internal', message: err.message } }, 500);\n }\n });\n\n // Register wildcard route to forward all auth requests to better-auth.\n // better-auth is configured with basePath matching our route prefix, so we\n // forward the original request directly — no path rewriting needed.\n rawApp.all(`${basePath}/*`, async (c: any) => {\n try {\n // Forward the original request to better-auth handler\n const response = await this.authManager!.handleRequest(c.req.raw);\n\n // better-auth catches internal errors and returns error Responses\n // without throwing, so the catch block below would never trigger.\n // We proactively log server errors here for observability.\n if (response.status >= 500) {\n try {\n const body = await response.clone().text();\n ctx.logger.error('[AuthPlugin] better-auth returned server error', new Error(`HTTP ${response.status}: ${body}`));\n } catch {\n ctx.logger.error('[AuthPlugin] better-auth returned server error', new Error(`HTTP ${response.status}: (unable to read body)`));\n }\n }\n\n // Public-cache JWKS: it's static JSON that only changes when the\n // signing key rotates (default ~30 days). better-auth doesn't set\n // any Cache-Control header, so every relying party currently\n // re-fetches it on every JWT verification (≈700 ms warm against a\n // Container DO + Neon). Add a conservative public cache so CF's\n // edge can short-circuit repeated fetches. The 5-min freshness +\n // 24 h SWR window is well inside better-auth's default rotation\n // and matches what most IdPs publish (Auth0, Cognito, Google).\n try {\n const url = c.req.url as string;\n if (response.ok && /\\/jwks(\\?|$)/.test(url)) {\n const existing = response.headers.get('cache-control');\n if (!existing) {\n response.headers.set(\n 'cache-control',\n 'public, max-age=300, stale-while-revalidate=86400',\n );\n }\n }\n } catch { /* best-effort header annotation */ }\n\n return response;\n } catch (error) {\n const err = error instanceof Error ? error : new Error(String(error));\n ctx.logger.error('Auth request error:', err);\n \n // Return error response\n return new Response(\n JSON.stringify({\n success: false,\n error: err.message,\n }),\n {\n status: 500,\n headers: { 'Content-Type': 'application/json' },\n }\n );\n }\n });\n\n // OIDC / OAuth 2.0 Authorization Server Metadata (RFC 8414) and\n // OpenID Connect Discovery 1.0 require the well-known documents to be\n // served from the **root** of the issuer URL — not under our auth\n // basePath. `@better-auth/oauth-provider` ships dedicated helpers for\n // this case (`oauthProviderAuthServerMetadata` /\n // `oauthProviderOpenIdConfigMetadata`) which we mount here so external\n // OIDC clients can discover the IdP at the canonical paths.\n if (this.options.plugins?.oidcProvider) {\n void this.registerOidcDiscoveryRoutes(rawApp, ctx).catch((error) => {\n ctx.logger.error('Failed to register OIDC discovery routes', error as Error);\n });\n }\n\n ctx.logger.info(`Auth routes registered: All requests under ${basePath}/* forwarded to better-auth`);\n }\n\n /**\n * Mount the OIDC / OAuth 2.0 well-known discovery documents at the root\n * URL. Required by RFC 8414 §3 and OpenID Connect Discovery 1.0 §4 — the\n * documents must live at `/.well-known/{oauth-authorization-server,openid-configuration}`\n * relative to the issuer, not under the auth basePath.\n */\n private async registerOidcDiscoveryRoutes(rawApp: any, ctx: PluginContext): Promise<void> {\n const auth = await this.authManager!.getAuthInstance();\n const { oauthProviderAuthServerMetadata, oauthProviderOpenIdConfigMetadata } = await import(\n '@better-auth/oauth-provider'\n );\n\n const authServerHandler = oauthProviderAuthServerMetadata(auth as any);\n const openidConfigHandler = oauthProviderOpenIdConfigMetadata(auth as any);\n\n // Cache-Control for OIDC discovery docs. These describe stable issuer\n // configuration (endpoints, supported scopes, signing algs); they\n // change only on app redeploy. CF edge can short-circuit repeated\n // fetches and dramatically cut SSO first-call latency.\n const DISCOVERY_CACHE = 'public, max-age=300, stale-while-revalidate=86400';\n const withDiscoveryCache = async (handler: (req: Request) => Promise<Response> | Response, req: Request): Promise<Response> => {\n const resp = await handler(req);\n try {\n if (resp.ok && !resp.headers.get('cache-control')) {\n resp.headers.set('cache-control', DISCOVERY_CACHE);\n }\n } catch { /* best-effort */ }\n return resp;\n };\n\n rawApp.get('/.well-known/oauth-authorization-server', (c: any) => withDiscoveryCache(authServerHandler, c.req.raw));\n rawApp.get('/.well-known/openid-configuration', (c: any) => withDiscoveryCache(openidConfigHandler, c.req.raw));\n\n ctx.logger.info(\n 'OIDC discovery endpoints mounted at /.well-known/{oauth-authorization-server,openid-configuration}',\n );\n }\n}\n\n\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport type { IDataEngine } from '@objectstack/core';\nimport { createAdapterFactory } from 'better-auth/adapters';\nimport type { CleanedWhere } from 'better-auth/adapters';\nimport { SystemObjectName } from '@objectstack/spec/system';\n\n/**\n * Mapping from better-auth model names to ObjectStack protocol object names.\n *\n * better-auth uses hardcoded model names ('user', 'session', 'account', 'verification')\n * while ObjectStack's protocol layer uses `sys_` prefixed names. This map bridges the two.\n */\nexport const AUTH_MODEL_TO_PROTOCOL: Record<string, string> = {\n user: SystemObjectName.USER,\n session: SystemObjectName.SESSION,\n account: SystemObjectName.ACCOUNT,\n verification: SystemObjectName.VERIFICATION,\n};\n\n/**\n * Resolve a better-auth model name to the ObjectStack protocol object name.\n * Falls back to the original model name for custom / non-core models.\n */\nexport function resolveProtocolName(model: string): string {\n return AUTH_MODEL_TO_PROTOCOL[model] ?? model;\n}\n\n// ---------------------------------------------------------------------------\n// Internal helpers\n// ---------------------------------------------------------------------------\n\n/**\n * better-auth datetime columns (snake_case) per model.\n *\n * When the underlying driver stored these as JavaScript `Date` objects\n * (legacy behaviour), the libsql HTTP transport coerces the value to a REAL\n * column and round-trips it as a string like `\"1779497911249.0\"`. That\n * string is not a valid Date string (it has a trailing `.0`), so\n * `new Date(...)` produces `Invalid Date` and better-auth's client treats\n * the session as expired — causing a login/redirect loop.\n *\n * We normalise these legacy values back to ISO strings on **read** so the\n * factory's `supportsDates: false` parser can turn them into real Date\n * objects. New writes always go through better-auth's own\n * `Date → ISO string` conversion (because we declare `supportsDates: false`\n * below), so no further `.0`-suffixed values will ever be created.\n */\nconst LEGACY_DATETIME_FIELDS_BY_MODEL: Record<string, string[]> = {\n user: ['created_at', 'updated_at'],\n session: ['expires_at', 'created_at', 'updated_at'],\n account: [\n 'access_token_expires_at',\n 'refresh_token_expires_at',\n 'created_at',\n 'updated_at',\n ],\n verification: ['expires_at', 'created_at', 'updated_at'],\n};\n\nconst NUMERIC_STRING_RE = /^-?\\d+(\\.\\d+)?$/;\n\n/**\n * If `value` looks like a stringified epoch-ms (optionally with `.0`),\n * convert it to an ISO 8601 string. Otherwise return it unchanged.\n */\nfunction normaliseLegacyDate(value: unknown): unknown {\n if (typeof value !== 'string') return value;\n if (!NUMERIC_STRING_RE.test(value)) return value;\n const n = parseFloat(value);\n if (!Number.isFinite(n)) return value;\n // Heuristic: epoch milliseconds are at least 10 digits (year 2001+).\n if (Math.abs(n) < 1e10) return value;\n const d = new Date(n);\n if (Number.isNaN(d.getTime())) return value;\n return d.toISOString();\n}\n\n/**\n * Walk a record and rewrite any legacy `.0`-suffixed datetime values\n * into ISO strings. Mutates and returns the record.\n */\nfunction normaliseLegacyDates<T extends Record<string, any> | null | undefined>(\n model: string,\n record: T,\n): T {\n if (!record) return record;\n const cols = LEGACY_DATETIME_FIELDS_BY_MODEL[model];\n if (!cols) return record;\n for (const col of cols) {\n if (col in record) {\n (record as Record<string, unknown>)[col] = normaliseLegacyDate(\n (record as Record<string, unknown>)[col],\n );\n }\n }\n return record;\n}\n\n/**\n * Convert better-auth where clause to ObjectQL query format.\n *\n * Field names in the incoming {@link CleanedWhere} are expected to already be\n * in snake_case (transformed by `createAdapterFactory`).\n */\nfunction convertWhere(where: CleanedWhere[]): Record<string, any> {\n const filter: Record<string, any> = {};\n\n for (const condition of where) {\n const fieldName = condition.field;\n\n if (condition.operator === 'eq') {\n filter[fieldName] = condition.value;\n } else if (condition.operator === 'ne') {\n filter[fieldName] = { $ne: condition.value };\n } else if (condition.operator === 'in') {\n filter[fieldName] = { $in: condition.value };\n } else if (condition.operator === 'gt') {\n filter[fieldName] = { $gt: condition.value };\n } else if (condition.operator === 'gte') {\n filter[fieldName] = { $gte: condition.value };\n } else if (condition.operator === 'lt') {\n filter[fieldName] = { $lt: condition.value };\n } else if (condition.operator === 'lte') {\n filter[fieldName] = { $lte: condition.value };\n } else if (condition.operator === 'contains') {\n filter[fieldName] = { $regex: condition.value };\n }\n }\n\n return filter;\n}\n\n// ---------------------------------------------------------------------------\n// Adapter factory\n// ---------------------------------------------------------------------------\n\n/**\n * Create an ObjectQL adapter **factory** for better-auth.\n *\n * Uses better-auth's official `createAdapterFactory` so that model-name and\n * field-name transformations (declared via `modelName` / `fields` in the\n * betterAuth config) are applied **automatically** before any data reaches\n * ObjectQL. This eliminates the need for manual camelCase ↔ snake_case\n * conversion inside the adapter.\n *\n * The returned value is an `AdapterFactory` – a function of type\n * `(options: BetterAuthOptions) => DBAdapter` – which is the shape expected\n * by `betterAuth({ database: … })`.\n *\n * @param dataEngine - ObjectQL data engine instance\n * @returns better-auth AdapterFactory\n */\nexport function createObjectQLAdapterFactory(dataEngine: IDataEngine) {\n return createAdapterFactory({\n config: {\n adapterId: 'objectql',\n // We let better-auth handle Date↔string and boolean↔0/1 conversion so\n // that values land in the underlying SQL driver as primitive strings\n // and integers. Some drivers (e.g. libsql over the HTTP transport)\n // otherwise mangle `Date` objects into `\"<epoch>.0\"` strings that\n // break the client-side session parser.\n supportsBooleans: false,\n supportsDates: false,\n supportsJSON: true,\n },\n adapter: () => ({\n create: async <T extends Record<string, any>>(\n { model, data, select: _select }: { model: string; data: T; select?: string[] },\n ): Promise<T> => {\n const result = await dataEngine.insert(model, data);\n return normaliseLegacyDates(model, result) as T;\n },\n\n findOne: async <T>(\n { model, where, select, join: _join }: { model: string; where: CleanedWhere[]; select?: string[]; join?: any },\n ): Promise<T | null> => {\n const filter = convertWhere(where);\n\n const result = await dataEngine.findOne(model, { where: filter, fields: select });\n\n return result ? (normaliseLegacyDates(model, result) as T) : null;\n },\n\n findMany: async <T>(\n { model, where, limit, offset, sortBy, join: _join }: {\n model: string; where?: CleanedWhere[]; limit: number;\n offset?: number; sortBy?: { field: string; direction: 'asc' | 'desc' }; join?: any;\n },\n ): Promise<T[]> => {\n const filter = where ? convertWhere(where) : {};\n\n const orderBy = sortBy\n ? [{ field: sortBy.field, order: sortBy.direction as 'asc' | 'desc' }]\n : undefined;\n\n const results = await dataEngine.find(model, {\n where: filter,\n limit: limit || 100,\n offset,\n orderBy,\n });\n\n return results.map((r) => normaliseLegacyDates(model, r as Record<string, any>)) as T[];\n },\n\n count: async (\n { model, where }: { model: string; where?: CleanedWhere[] },\n ): Promise<number> => {\n const filter = where ? convertWhere(where) : {};\n return await dataEngine.count(model, { where: filter });\n },\n\n update: async <T>(\n { model, where, update }: { model: string; where: CleanedWhere[]; update: T },\n ): Promise<T | null> => {\n const filter = convertWhere(where);\n\n // ObjectQL requires an ID for updates – find the record first\n const record = await dataEngine.findOne(model, { where: filter });\n if (!record) return null;\n\n const result = await dataEngine.update(model, { ...(update as any), id: record.id });\n return result ? (normaliseLegacyDates(model, result) as T) : null;\n },\n\n updateMany: async (\n { model, where, update }: { model: string; where: CleanedWhere[]; update: Record<string, any> },\n ): Promise<number> => {\n const filter = convertWhere(where);\n\n // Sequential updates: ObjectQL requires an ID per update\n const records = await dataEngine.find(model, { where: filter });\n for (const record of records) {\n await dataEngine.update(model, { ...update, id: record.id });\n }\n return records.length;\n },\n\n delete: async (\n { model, where }: { model: string; where: CleanedWhere[] },\n ): Promise<void> => {\n const filter = convertWhere(where);\n\n const record = await dataEngine.findOne(model, { where: filter });\n if (!record) return;\n\n await dataEngine.delete(model, { where: { id: record.id } });\n },\n\n deleteMany: async (\n { model, where }: { model: string; where: CleanedWhere[] },\n ): Promise<number> => {\n const filter = convertWhere(where);\n\n const records = await dataEngine.find(model, { where: filter });\n for (const record of records) {\n await dataEngine.delete(model, { where: { id: record.id } });\n }\n return records.length;\n },\n }),\n });\n}\n\n// ---------------------------------------------------------------------------\n// Legacy adapter (kept for backward compatibility)\n// ---------------------------------------------------------------------------\n\n/**\n * Create a raw ObjectQL adapter for better-auth (without factory wrapping).\n *\n * > **Prefer {@link createObjectQLAdapterFactory}** for production use.\n * > The factory version leverages `createAdapterFactory` and automatically\n * > handles model-name + field-name transformations declared in the\n * > better-auth config.\n *\n * This function is retained for direct / low-level usage where callers\n * manage field-name conversion themselves.\n *\n * @param dataEngine - ObjectQL data engine instance\n * @returns better-auth CustomAdapter (raw, without factory wrapping)\n */\nexport function createObjectQLAdapter(dataEngine: IDataEngine) {\n return {\n create: async <T extends Record<string, any>>({ model, data, select: _select }: { model: string; data: T; select?: string[] }): Promise<T> => {\n const objectName = resolveProtocolName(model);\n const result = await dataEngine.insert(objectName, data);\n return result as T;\n },\n\n findOne: async <T>({ model, where, select, join: _join }: { model: string; where: CleanedWhere[]; select?: string[]; join?: any }): Promise<T | null> => {\n const objectName = resolveProtocolName(model);\n const filter = convertWhere(where);\n const result = await dataEngine.findOne(objectName, { where: filter, fields: select });\n return result ? result as T : null;\n },\n\n findMany: async <T>({ model, where, limit, offset, sortBy, join: _join }: { model: string; where?: CleanedWhere[]; limit: number; offset?: number; sortBy?: { field: string; direction: 'asc' | 'desc' }; join?: any }): Promise<T[]> => {\n const objectName = resolveProtocolName(model);\n const filter = where ? convertWhere(where) : {};\n const orderBy = sortBy ? [{ field: sortBy.field, order: sortBy.direction as 'asc' | 'desc' }] : undefined;\n const results = await dataEngine.find(objectName, { where: filter, limit: limit || 100, offset, orderBy });\n return results as T[];\n },\n\n count: async ({ model, where }: { model: string; where?: CleanedWhere[] }): Promise<number> => {\n const objectName = resolveProtocolName(model);\n const filter = where ? convertWhere(where) : {};\n return await dataEngine.count(objectName, { where: filter });\n },\n\n update: async <T>({ model, where, update }: { model: string; where: CleanedWhere[]; update: Record<string, any> }): Promise<T | null> => {\n const objectName = resolveProtocolName(model);\n const filter = convertWhere(where);\n const record = await dataEngine.findOne(objectName, { where: filter });\n if (!record) return null;\n const result = await dataEngine.update(objectName, { ...update, id: record.id });\n return result ? result as T : null;\n },\n\n updateMany: async ({ model, where, update }: { model: string; where: CleanedWhere[]; update: Record<string, any> }): Promise<number> => {\n const objectName = resolveProtocolName(model);\n const filter = convertWhere(where);\n const records = await dataEngine.find(objectName, { where: filter });\n for (const record of records) {\n await dataEngine.update(objectName, { ...update, id: record.id });\n }\n return records.length;\n },\n\n delete: async ({ model, where }: { model: string; where: CleanedWhere[] }): Promise<void> => {\n const objectName = resolveProtocolName(model);\n const filter = convertWhere(where);\n const record = await dataEngine.findOne(objectName, { where: filter });\n if (!record) return;\n await dataEngine.delete(objectName, { where: { id: record.id } });\n },\n\n deleteMany: async ({ model, where }: { model: string; where: CleanedWhere[] }): Promise<number> => {\n const objectName = resolveProtocolName(model);\n const filter = convertWhere(where);\n const records = await dataEngine.find(objectName, { where: filter });\n for (const record of records) {\n await dataEngine.delete(objectName, { where: { id: record.id } });\n }\n return records.length;\n },\n };\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport { SystemObjectName } from '@objectstack/spec/system';\n\n/**\n * better-auth ↔ ObjectStack Schema Mapping\n *\n * better-auth uses camelCase field names internally (e.g. `emailVerified`, `userId`)\n * while ObjectStack's protocol layer uses snake_case (e.g. `email_verified`, `user_id`).\n *\n * These constants declare the `modelName` and `fields` mappings for each core auth\n * model, following better-auth's official schema customisation API\n * ({@link https://www.better-auth.com/docs/concepts/database}).\n *\n * The mappings serve two purposes:\n * 1. `modelName` — maps the default model name to the ObjectStack protocol name\n * (e.g. `user` → `sys_user`).\n * 2. `fields` — maps camelCase field names to their snake_case database column\n * equivalents. Only fields whose names differ need to be listed; fields that\n * are already identical (e.g. `email`, `name`, `token`) are omitted.\n *\n * These mappings are consumed by:\n * - The `betterAuth()` configuration in {@link AuthManager} so that\n * `getAuthTables()` builds the correct schema.\n * - The ObjectQL adapter factory (via `createAdapterFactory`) which uses the\n * schema to transform data and where-clauses automatically.\n */\n\n// ---------------------------------------------------------------------------\n// User model\n// ---------------------------------------------------------------------------\n\n/**\n * better-auth `user` model mapping.\n *\n * | camelCase (better-auth) | snake_case (ObjectStack) |\n * |:------------------------|:-------------------------|\n * | emailVerified | email_verified |\n * | createdAt | created_at |\n * | updatedAt | updated_at |\n */\nexport const AUTH_USER_CONFIG = {\n modelName: SystemObjectName.USER, // 'sys_user'\n fields: {\n emailVerified: 'email_verified',\n createdAt: 'created_at',\n updatedAt: 'updated_at',\n },\n} as const;\n\n// ---------------------------------------------------------------------------\n// Session model\n// ---------------------------------------------------------------------------\n\n/**\n * better-auth `session` model mapping.\n *\n * | camelCase (better-auth) | snake_case (ObjectStack) |\n * |:------------------------|:-------------------------|\n * | userId | user_id |\n * | expiresAt | expires_at |\n * | createdAt | created_at |\n * | updatedAt | updated_at |\n * | ipAddress | ip_address |\n * | userAgent | user_agent |\n */\nexport const AUTH_SESSION_CONFIG = {\n modelName: SystemObjectName.SESSION, // 'sys_session'\n fields: {\n userId: 'user_id',\n expiresAt: 'expires_at',\n createdAt: 'created_at',\n updatedAt: 'updated_at',\n ipAddress: 'ip_address',\n userAgent: 'user_agent',\n },\n} as const;\n\n// ---------------------------------------------------------------------------\n// Account model\n// ---------------------------------------------------------------------------\n\n/**\n * better-auth `account` model mapping.\n *\n * | camelCase (better-auth) | snake_case (ObjectStack) |\n * |:--------------------------|:-------------------------------|\n * | userId | user_id |\n * | providerId | provider_id |\n * | accountId | account_id |\n * | accessToken | access_token |\n * | refreshToken | refresh_token |\n * | idToken | id_token |\n * | accessTokenExpiresAt | access_token_expires_at |\n * | refreshTokenExpiresAt | refresh_token_expires_at |\n * | createdAt | created_at |\n * | updatedAt | updated_at |\n */\nexport const AUTH_ACCOUNT_CONFIG = {\n modelName: SystemObjectName.ACCOUNT, // 'sys_account'\n fields: {\n userId: 'user_id',\n providerId: 'provider_id',\n accountId: 'account_id',\n accessToken: 'access_token',\n refreshToken: 'refresh_token',\n idToken: 'id_token',\n accessTokenExpiresAt: 'access_token_expires_at',\n refreshTokenExpiresAt: 'refresh_token_expires_at',\n createdAt: 'created_at',\n updatedAt: 'updated_at',\n },\n} as const;\n\n// ---------------------------------------------------------------------------\n// Verification model\n// ---------------------------------------------------------------------------\n\n/**\n * better-auth `verification` model mapping.\n *\n * | camelCase (better-auth) | snake_case (ObjectStack) |\n * |:------------------------|:-------------------------|\n * | expiresAt | expires_at |\n * | createdAt | created_at |\n * | updatedAt | updated_at |\n */\nexport const AUTH_VERIFICATION_CONFIG = {\n modelName: SystemObjectName.VERIFICATION, // 'sys_verification'\n fields: {\n expiresAt: 'expires_at',\n createdAt: 'created_at',\n updatedAt: 'updated_at',\n },\n} as const;\n\n// ===========================================================================\n// Plugin Table Mappings\n// ===========================================================================\n//\n// better-auth plugins (organization, two-factor, etc.) introduce additional\n// tables with their own camelCase field names. The mappings below are passed\n// to the plugin's `schema` option so that `createAdapterFactory` transforms\n// them to snake_case automatically, just like the core models above.\n// ===========================================================================\n\n// ---------------------------------------------------------------------------\n// Organization plugin – organization table\n// ---------------------------------------------------------------------------\n\n/**\n * better-auth Organization plugin `organization` model mapping.\n *\n * | camelCase (better-auth) | snake_case (ObjectStack) |\n * |:------------------------|:-------------------------|\n * | createdAt | created_at |\n * | updatedAt | updated_at |\n */\nexport const AUTH_ORGANIZATION_SCHEMA = {\n modelName: SystemObjectName.ORGANIZATION, // 'sys_organization'\n fields: {\n createdAt: 'created_at',\n updatedAt: 'updated_at',\n },\n} as const;\n\n// ---------------------------------------------------------------------------\n// Organization plugin – member table\n// ---------------------------------------------------------------------------\n\n/**\n * better-auth Organization plugin `member` model mapping.\n *\n * | camelCase (better-auth) | snake_case (ObjectStack) |\n * |:------------------------|:-------------------------|\n * | organizationId | organization_id |\n * | userId | user_id |\n * | createdAt | created_at |\n */\nexport const AUTH_MEMBER_SCHEMA = {\n modelName: SystemObjectName.MEMBER, // 'sys_member'\n fields: {\n organizationId: 'organization_id',\n userId: 'user_id',\n createdAt: 'created_at',\n },\n} as const;\n\n// ---------------------------------------------------------------------------\n// Organization plugin – invitation table\n// ---------------------------------------------------------------------------\n\n/**\n * better-auth Organization plugin `invitation` model mapping.\n *\n * | camelCase (better-auth) | snake_case (ObjectStack) |\n * |:------------------------|:-------------------------|\n * | organizationId | organization_id |\n * | inviterId | inviter_id |\n * | expiresAt | expires_at |\n * | createdAt | created_at |\n * | teamId | team_id |\n */\nexport const AUTH_INVITATION_SCHEMA = {\n modelName: SystemObjectName.INVITATION, // 'sys_invitation'\n fields: {\n organizationId: 'organization_id',\n inviterId: 'inviter_id',\n expiresAt: 'expires_at',\n createdAt: 'created_at',\n teamId: 'team_id',\n },\n} as const;\n\n// ---------------------------------------------------------------------------\n// Organization plugin – session additional fields\n// ---------------------------------------------------------------------------\n\n/**\n * Organization plugin adds `activeOrganizationId` (and optionally\n * `activeTeamId`) to the session model. These field mappings are\n * injected via the organization plugin's `schema.session.fields`.\n */\nexport const AUTH_ORG_SESSION_FIELDS = {\n activeOrganizationId: 'active_organization_id',\n activeTeamId: 'active_team_id',\n} as const;\n\n// ---------------------------------------------------------------------------\n// Organization plugin – team table (optional, when teams enabled)\n// ---------------------------------------------------------------------------\n\n/**\n * better-auth Organization plugin `team` model mapping.\n *\n * | camelCase (better-auth) | snake_case (ObjectStack) |\n * |:------------------------|:-------------------------|\n * | organizationId | organization_id |\n * | createdAt | created_at |\n * | updatedAt | updated_at |\n */\nexport const AUTH_TEAM_SCHEMA = {\n modelName: SystemObjectName.TEAM, // 'sys_team'\n fields: {\n organizationId: 'organization_id',\n createdAt: 'created_at',\n updatedAt: 'updated_at',\n },\n} as const;\n\n// ---------------------------------------------------------------------------\n// Organization plugin – teamMember table (optional, when teams enabled)\n// ---------------------------------------------------------------------------\n\n/**\n * better-auth Organization plugin `teamMember` model mapping.\n *\n * | camelCase (better-auth) | snake_case (ObjectStack) |\n * |:------------------------|:-------------------------|\n * | teamId | team_id |\n * | userId | user_id |\n * | createdAt | created_at |\n */\nexport const AUTH_TEAM_MEMBER_SCHEMA = {\n modelName: SystemObjectName.TEAM_MEMBER, // 'sys_team_member'\n fields: {\n teamId: 'team_id',\n userId: 'user_id',\n createdAt: 'created_at',\n },\n} as const;\n\n// ---------------------------------------------------------------------------\n// Two-Factor plugin – twoFactor table\n// ---------------------------------------------------------------------------\n\n/**\n * better-auth Two-Factor plugin `twoFactor` model mapping.\n *\n * | camelCase (better-auth) | snake_case (ObjectStack) |\n * |:------------------------|:-------------------------|\n * | backupCodes | backup_codes |\n * | userId | user_id |\n */\nexport const AUTH_TWO_FACTOR_SCHEMA = {\n modelName: SystemObjectName.TWO_FACTOR, // 'sys_two_factor'\n fields: {\n backupCodes: 'backup_codes',\n userId: 'user_id',\n },\n} as const;\n\n/**\n * Two-Factor plugin adds a `twoFactorEnabled` field to the user model.\n */\nexport const AUTH_TWO_FACTOR_USER_FIELDS = {\n twoFactorEnabled: 'two_factor_enabled',\n} as const;\n\n// ---------------------------------------------------------------------------\n// Admin plugin – user/session field additions\n// ---------------------------------------------------------------------------\n\n/**\n * Admin plugin adds platform-level admin fields to the `user` model.\n *\n * | camelCase (better-auth) | snake_case (ObjectStack) |\n * |:------------------------|:-------------------------|\n * | banReason | ban_reason |\n * | banExpires | ban_expires |\n *\n * `role` and `banned` already have matching snake_case names and are\n * therefore omitted from this mapping (better-auth's database hooks\n * read them by the auto-derived column names).\n */\nexport const AUTH_ADMIN_USER_FIELDS = {\n banReason: 'ban_reason',\n banExpires: 'ban_expires',\n} as const;\n\n/**\n * Admin plugin adds an `impersonatedBy` field to the session model\n * recording the operator user id when an admin impersonates someone.\n */\nexport const AUTH_ADMIN_SESSION_FIELDS = {\n impersonatedBy: 'impersonated_by',\n} as const;\n\n// ---------------------------------------------------------------------------\n// OAuth Provider plugin – oauthClient table\n// ---------------------------------------------------------------------------\n\n/**\n * `@better-auth/oauth-provider` plugin `oauthClient` model mapping.\n *\n * The model name (`oauthClient`) is mapped to the existing\n * `sys_oauth_application` table to preserve data continuity from the\n * deprecated `oidc-provider` plugin.\n *\n * | camelCase (better-auth) | snake_case (ObjectStack) |\n * |:---------------------------|:--------------------------------|\n * | clientId | client_id |\n * | clientSecret | client_secret |\n * | skipConsent | skip_consent |\n * | enableEndSession | enable_end_session |\n * | subjectType | subject_type |\n * | userId | user_id |\n * | createdAt | created_at |\n * | updatedAt | updated_at |\n * | redirectUris | redirect_uris |\n * | postLogoutRedirectUris | post_logout_redirect_uris |\n * | tokenEndpointAuthMethod | token_endpoint_auth_method |\n * | grantTypes | grant_types |\n * | responseTypes | response_types |\n * | requirePKCE | require_pkce |\n * | softwareId | software_id |\n * | softwareVersion | software_version |\n * | softwareStatement | software_statement |\n * | referenceId | reference_id |\n */\nexport const AUTH_OAUTH_CLIENT_SCHEMA = {\n modelName: SystemObjectName.OAUTH_APPLICATION, // 'sys_oauth_application'\n fields: {\n clientId: 'client_id',\n clientSecret: 'client_secret',\n skipConsent: 'skip_consent',\n enableEndSession: 'enable_end_session',\n subjectType: 'subject_type',\n userId: 'user_id',\n createdAt: 'created_at',\n updatedAt: 'updated_at',\n redirectUris: 'redirect_uris',\n postLogoutRedirectUris: 'post_logout_redirect_uris',\n tokenEndpointAuthMethod: 'token_endpoint_auth_method',\n grantTypes: 'grant_types',\n responseTypes: 'response_types',\n requirePKCE: 'require_pkce',\n softwareId: 'software_id',\n softwareVersion: 'software_version',\n softwareStatement: 'software_statement',\n referenceId: 'reference_id',\n },\n} as const;\n\n/**\n * @deprecated Use {@link AUTH_OAUTH_CLIENT_SCHEMA}. Retained as an alias for\n * historical imports; the new package renamed `oauthApplication` → `oauthClient`.\n */\nexport const AUTH_OAUTH_APPLICATION_SCHEMA = AUTH_OAUTH_CLIENT_SCHEMA;\n\n// ---------------------------------------------------------------------------\n// OAuth Provider plugin – oauthAccessToken table\n// ---------------------------------------------------------------------------\n\n/**\n * `@better-auth/oauth-provider` plugin `oauthAccessToken` model mapping.\n *\n * In the new package, access tokens and refresh tokens are stored in\n * **separate** models. `oauthAccessToken` no longer carries a refresh token;\n * see {@link AUTH_OAUTH_REFRESH_TOKEN_SCHEMA} for the companion model.\n *\n * | camelCase (better-auth) | snake_case (ObjectStack) |\n * |:------------------------|:-------------------------|\n * | clientId | client_id |\n * | sessionId | session_id |\n * | userId | user_id |\n * | referenceId | reference_id |\n * | refreshId | refresh_id |\n * | expiresAt | expires_at |\n * | createdAt | created_at |\n */\nexport const AUTH_OAUTH_ACCESS_TOKEN_SCHEMA = {\n modelName: SystemObjectName.OAUTH_ACCESS_TOKEN, // 'sys_oauth_access_token'\n fields: {\n clientId: 'client_id',\n sessionId: 'session_id',\n userId: 'user_id',\n referenceId: 'reference_id',\n refreshId: 'refresh_id',\n expiresAt: 'expires_at',\n createdAt: 'created_at',\n },\n} as const;\n\n// ---------------------------------------------------------------------------\n// OAuth Provider plugin – oauthRefreshToken table\n// ---------------------------------------------------------------------------\n\n/**\n * `@better-auth/oauth-provider` plugin `oauthRefreshToken` model mapping.\n *\n * Refresh tokens are linked to a session (via `session_id`) and to the\n * issuing client. Each access token rotation produces a new refresh-token\n * row.\n *\n * | camelCase (better-auth) | snake_case (ObjectStack) |\n * |:------------------------|:-------------------------|\n * | clientId | client_id |\n * | sessionId | session_id |\n * | userId | user_id |\n * | referenceId | reference_id |\n * | expiresAt | expires_at |\n * | createdAt | created_at |\n * | authTime | auth_time |\n */\nexport const AUTH_OAUTH_REFRESH_TOKEN_SCHEMA = {\n modelName: SystemObjectName.OAUTH_REFRESH_TOKEN, // 'sys_oauth_refresh_token'\n fields: {\n clientId: 'client_id',\n sessionId: 'session_id',\n userId: 'user_id',\n referenceId: 'reference_id',\n expiresAt: 'expires_at',\n createdAt: 'created_at',\n authTime: 'auth_time',\n },\n} as const;\n\n// ---------------------------------------------------------------------------\n// OAuth Provider plugin – oauthConsent table\n// ---------------------------------------------------------------------------\n\n/**\n * `@better-auth/oauth-provider` plugin `oauthConsent` model mapping.\n *\n * The new package dropped the boolean `consentGiven` flag — the presence of\n * a row implies consent was given for the listed scopes. A new\n * `referenceId` column was added for client-supplied correlation.\n *\n * | camelCase (better-auth) | snake_case (ObjectStack) |\n * |:------------------------|:-------------------------|\n * | clientId | client_id |\n * | userId | user_id |\n * | referenceId | reference_id |\n * | createdAt | created_at |\n * | updatedAt | updated_at |\n */\nexport const AUTH_OAUTH_CONSENT_SCHEMA = {\n modelName: SystemObjectName.OAUTH_CONSENT, // 'sys_oauth_consent'\n fields: {\n clientId: 'client_id',\n userId: 'user_id',\n referenceId: 'reference_id',\n createdAt: 'created_at',\n updatedAt: 'updated_at',\n },\n} as const;\n\n// ---------------------------------------------------------------------------\n// Device Authorization plugin – deviceCode table\n// ---------------------------------------------------------------------------\n\n/**\n * better-auth `device-authorization` plugin `deviceCode` model mapping.\n *\n * Implements RFC 8628 (OAuth 2.0 Device Authorization Grant). Stores\n * pending device-flow requests issued via `POST /device/code`, polled at\n * `POST /device/token`, and approved/denied via `POST /device/{approve,deny}`.\n *\n * | camelCase (better-auth) | snake_case (ObjectStack) |\n * |:------------------------|:-------------------------|\n * | deviceCode | device_code |\n * | userCode | user_code |\n * | userId | user_id |\n * | expiresAt | expires_at |\n * | lastPolledAt | last_polled_at |\n * | pollingInterval | polling_interval |\n * | clientId | client_id |\n */\nexport const AUTH_DEVICE_CODE_SCHEMA = {\n modelName: SystemObjectName.DEVICE_CODE, // 'sys_device_code'\n fields: {\n deviceCode: 'device_code',\n userCode: 'user_code',\n userId: 'user_id',\n expiresAt: 'expires_at',\n lastPolledAt: 'last_polled_at',\n pollingInterval: 'polling_interval',\n clientId: 'client_id',\n },\n} as const;\n\n/**\n * Builds the `schema` option for better-auth's `twoFactor()` plugin.\n *\n * @returns An object suitable for `twoFactor({ schema: … })`\n */\nexport function buildTwoFactorPluginSchema() {\n return {\n twoFactor: AUTH_TWO_FACTOR_SCHEMA,\n user: {\n fields: AUTH_TWO_FACTOR_USER_FIELDS,\n },\n };\n}\n\n/**\n * Builds the `schema` option for better-auth's `admin()` plugin.\n *\n * The admin plugin extends the user model with `role`/`banned`/`banReason`/\n * `banExpires` and the session model with `impersonatedBy`. Only the\n * snake_case-differing fields are mapped explicitly.\n */\nexport function buildAdminPluginSchema() {\n return {\n user: {\n fields: AUTH_ADMIN_USER_FIELDS,\n },\n session: {\n fields: AUTH_ADMIN_SESSION_FIELDS,\n },\n };\n}\n\n// ---------------------------------------------------------------------------\n// Helper: build organization plugin schema option\n// ---------------------------------------------------------------------------\n\n/**\n * Builds the `schema` option for better-auth's `organization()` plugin.\n *\n * The organization plugin accepts a `schema` sub-option that allows\n * customising model names and field names for each table it manages.\n * This helper assembles the correct snake_case mappings from the\n * individual `AUTH_*_SCHEMA` constants above.\n *\n * @returns An object suitable for `organization({ schema: … })`\n */\nexport function buildOrganizationPluginSchema() {\n return {\n organization: AUTH_ORGANIZATION_SCHEMA,\n member: AUTH_MEMBER_SCHEMA,\n invitation: AUTH_INVITATION_SCHEMA,\n team: AUTH_TEAM_SCHEMA,\n teamMember: AUTH_TEAM_MEMBER_SCHEMA,\n session: {\n fields: AUTH_ORG_SESSION_FIELDS,\n },\n };\n}\n\n// ---------------------------------------------------------------------------\n// JWT plugin – jwks table\n// ---------------------------------------------------------------------------\n\n/**\n * better-auth `jwt` plugin `jwks` model mapping.\n *\n * The JWT plugin maintains a small set of rotating asymmetric key pairs\n * used to sign and verify issued JWTs (id_tokens for OIDC, JWT access\n * tokens). It is required by the `@better-auth/oauth-provider` plugin.\n *\n * | camelCase (better-auth) | snake_case (ObjectStack) |\n * |:------------------------|:-------------------------|\n * | publicKey | public_key |\n * | privateKey | private_key |\n * | createdAt | created_at |\n * | expiresAt | expires_at |\n */\nexport const AUTH_JWKS_SCHEMA = {\n modelName: SystemObjectName.JWKS, // 'sys_jwks'\n fields: {\n publicKey: 'public_key',\n privateKey: 'private_key',\n createdAt: 'created_at',\n expiresAt: 'expires_at',\n },\n} as const;\n\n/**\n * Builds the `schema` option for better-auth's `jwt()` plugin.\n *\n * @returns An object suitable for `jwt({ schema: … })`\n */\nexport function buildJwtPluginSchema() {\n return {\n jwks: AUTH_JWKS_SCHEMA,\n };\n}\n\n// ---------------------------------------------------------------------------\n// Helper: build OAuth provider plugin schema option\n// ---------------------------------------------------------------------------\n\n/**\n * Builds the `schema` option for `@better-auth/oauth-provider`'s\n * `oauthProvider()` plugin.\n *\n * The plugin manages four tables: `oauthClient` (registered client apps —\n * mapped to ObjectStack's `sys_oauth_application` table for backwards\n * compatibility), `oauthAccessToken` (issued access tokens),\n * `oauthRefreshToken` (issued refresh tokens, linked to a session), and\n * `oauthConsent` (recorded user consents).\n *\n * @returns An object suitable for `oauthProvider({ schema: … })`\n */\nexport function buildOauthProviderPluginSchema() {\n return {\n oauthClient: AUTH_OAUTH_CLIENT_SCHEMA,\n oauthAccessToken: AUTH_OAUTH_ACCESS_TOKEN_SCHEMA,\n oauthRefreshToken: AUTH_OAUTH_REFRESH_TOKEN_SCHEMA,\n oauthConsent: AUTH_OAUTH_CONSENT_SCHEMA,\n };\n}\n\n/**\n * @deprecated Use {@link buildOauthProviderPluginSchema}. Retained as an\n * alias for callers that imported the previous name during the migration\n * from the deprecated `better-auth/plugins/oidc-provider` plugin.\n */\nexport const buildOidcProviderPluginSchema = buildOauthProviderPluginSchema;\n\n// ---------------------------------------------------------------------------\n// Helper: build device-authorization plugin schema option\n// ---------------------------------------------------------------------------\n\n/**\n * Builds the `schema` option for better-auth's `deviceAuthorization()` plugin.\n *\n * The plugin manages a single `deviceCode` table tracking pending RFC 8628\n * device-flow requests. This helper returns the snake_case mappings that\n * point the plugin at ObjectStack's `sys_device_code` object.\n *\n * @returns An object suitable for `deviceAuthorization({ schema: … })`\n */\nexport function buildDeviceAuthorizationPluginSchema() {\n return {\n deviceCode: AUTH_DEVICE_CODE_SCHEMA,\n };\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport type { Auth, BetterAuthOptions } from 'better-auth';\n// better-auth value imports (betterAuth + plugins) are deferred via dynamic\n// import() in getOrCreateAuth() / buildPluginList() so that disabled plugins\n// never get loaded into the process. See Stage 2F (RSS investigation).\nimport type {\n AuthConfig,\n EmailAndPasswordConfig,\n AuthPluginConfig,\n OidcProvidersConfig,\n} from '@objectstack/spec/system';\nimport type { IDataEngine } from '@objectstack/core';\nimport type { IEmailService } from '@objectstack/spec/contracts';\nimport { createObjectQLAdapterFactory } from './objectql-adapter.js';\nimport {\n AUTH_USER_CONFIG,\n AUTH_SESSION_CONFIG,\n AUTH_ACCOUNT_CONFIG,\n AUTH_VERIFICATION_CONFIG,\n buildOrganizationPluginSchema,\n buildTwoFactorPluginSchema,\n buildOauthProviderPluginSchema,\n buildDeviceAuthorizationPluginSchema,\n buildJwtPluginSchema,\n buildAdminPluginSchema,\n} from './auth-schema-config.js';\n\n/**\n * Detect WebContainer (StackBlitz) environment.\n *\n * WebContainer reports itself as Node.js but runs inside a browser. Several\n * Node APIs are polyfilled with significant behavioural differences — most\n * notably `node:async_hooks.AsyncLocalStorage`, whose `run()` does NOT\n * propagate the store across `await` boundaries the way Node's native\n * implementation does.\n */\nfunction isWebContainerRuntime(): boolean {\n if (typeof globalThis === 'undefined') return false;\n const proc = (globalThis as any).process;\n return (\n Boolean(proc?.versions?.webcontainer) ||\n Boolean(proc?.env?.SHELL?.includes?.('jsh')) ||\n Boolean(proc?.env?.STACKBLITZ)\n );\n}\n\n/**\n * Synchronous AsyncLocalStorage polyfill compatible with better-auth's\n * `requestStateAsyncStorage` slot.\n *\n * Behaviour:\n * - `run(store, fn)` sets the current store synchronously before invoking\n * `fn` and restores the previous store after `fn` (and any promise it\n * returns) settles.\n * - `getStore()` returns the current store.\n *\n * Why a polyfill is needed in WebContainer:\n * - WebContainer's `node:async_hooks` does not propagate ALS context through\n * `await`, so better-auth's `runWithRequestState(map, () => handler(req))`\n * wrap loses the store as soon as the call chain awaits anything (e.g.\n * the inner `customSession` → `getSession()` call). All endpoints that\n * read request-state via `defineRequestState()` then throw\n * \"No request state found\".\n *\n * Single-flight caveat:\n * - This polyfill is process-global, not async-context-local. In a real\n * server it could leak state across concurrent requests. That risk is\n * acceptable here because:\n * 1) It is only installed when WebContainer is detected (dev / preview\n * sandboxes that handle one request at a time).\n * 2) Each request still wraps the entire handler in `runWithRequestState`\n * with a fresh WeakMap, so the in-flight request always sees its own\n * store as long as nothing else mutates the slot mid-flight.\n */\nclass WebContainerRequestStateAsyncLocalStorage<T> {\n private current: T | undefined = undefined;\n\n run<R>(store: T, fn: () => R): R {\n const prev = this.current;\n this.current = store;\n try {\n const result = fn() as unknown;\n if (result && typeof (result as Promise<unknown>).then === 'function') {\n return (result as Promise<unknown>).finally(() => {\n this.current = prev;\n }) as unknown as R;\n }\n this.current = prev;\n return result as R;\n } catch (err) {\n this.current = prev;\n throw err;\n }\n }\n\n getStore(): T | undefined {\n return this.current;\n }\n}\n\n/**\n * Pre-populate better-auth's global `requestStateAsyncStorage` slot with the\n * synchronous polyfill when running inside WebContainer.\n *\n * Better-auth caches its AsyncLocalStorage instance on\n * `globalThis[Symbol.for('better-auth:global')].context.requestStateAsyncStorage`\n * the first time `ensureAsyncStorage()` runs (see\n * `@better-auth/core/dist/context/request-state.mjs`). By seeding that slot\n * BEFORE any better-auth code touches it, every call to\n * `runWithRequestState` / `getCurrentRequestState` — including the\n * `@better-auth/core` copy that `plugin-auth` imports directly and the copy\n * bundled with `better-auth` itself — share the same working polyfill.\n *\n * Outside WebContainer this is a no-op so production deployments keep\n * Node's native AsyncLocalStorage.\n */\nfunction installWebContainerRequestStatePolyfill(): void {\n if (!isWebContainerRuntime()) return;\n const sym = Symbol.for('better-auth:global');\n const g = globalThis as any;\n if (!g[sym]) {\n g[sym] = { version: '0.0.0-polyfill', epoch: 0, context: {} };\n }\n if (!g[sym].context) g[sym].context = {};\n if (!g[sym].context.requestStateAsyncStorage) {\n g[sym].context.requestStateAsyncStorage = new WebContainerRequestStateAsyncLocalStorage();\n // eslint-disable-next-line no-console\n console.warn(\n '[AuthManager] WebContainer detected: installed synchronous request-state polyfill ' +\n '(node:async_hooks AsyncLocalStorage does not propagate context across await in WebContainer).',\n );\n }\n}\n\n/**\n * Extended options for AuthManager\n */\nexport interface AuthManagerOptions extends Partial<AuthConfig> {\n /**\n * Better-Auth instance (for advanced use cases)\n * If not provided, one will be created from config\n */\n authInstance?: Auth<any>;\n\n /**\n * ObjectQL Data Engine instance\n * Required for database operations using ObjectQL instead of third-party ORMs\n */\n dataEngine?: IDataEngine;\n\n /**\n * Base path for auth routes\n * Forwarded to better-auth's basePath option so it can match incoming\n * request URLs without manual path rewriting.\n * @default '/api/v1/auth'\n */\n basePath?: string;\n\n /**\n * OIDC / Generic OAuth2 providers for enterprise SSO.\n * Each entry is passed to better-auth's genericOAuth plugin.\n */\n oidcProviders?: OidcProvidersConfig;\n\n /**\n * Application-specific organization roles to register with Better-Auth's\n * organization plugin. Each name becomes a valid role for invitations and\n * member assignments without going through Better-Auth's default\n * `owner|admin|member` whitelist.\n *\n * The ObjectStack SecurityPlugin handles real RBAC enforcement by matching\n * these role names against `permission` metadata (PermissionSets / Profiles),\n * so Better-Auth only needs to accept them as opaque strings. Each role is\n * registered with the minimum access-control privileges (equivalent to\n * Better-Auth's `member` role) so it cannot inadvertently grant org-level\n * admin capabilities.\n *\n * Typical source: the union of `permission` metadata names that have\n * `isProfile: true`, collected from the loaded stack at CLI boot.\n *\n * @example ['sales_rep', 'sales_manager', 'service_agent']\n */\n additionalOrgRoles?: string[];\n\n /**\n * Optional outbound email service used by better-auth callbacks\n * (`sendResetPassword`, `sendVerificationEmail`, `sendInvitationEmail`,\n * `sendMagicLink`). When omitted, those callbacks degrade to logging\n * the action URL — keeping flows usable in pilots / local dev — but\n * production deployments SHOULD wire one via `setEmailService()`.\n *\n * Resolved lazily through {@link AuthManager.getEmailService}; safe\n * to set after construction. AuthPlugin wires this from the kernel\n * service registry on `kernel:ready`.\n */\n emailService?: IEmailService;\n\n /**\n * Display name used by built-in auth email templates (`{{appName}}`\n * placeholder). Defaults to `'ObjectStack'` when omitted.\n */\n appName?: string;\n\n /**\n * Pass-through to better-auth's `databaseHooks` option. better-auth fires\n * these around its own adapter writes (e.g. when `genericOAuth` creates\n * a JIT user during SSO login), which the kernel-level ObjectQL\n * middleware does NOT observe — better-auth's adapter goes through\n * `dataEngine` directly, bypassing the `ql.registerMiddleware` chain.\n *\n * The platform uses this to attach a `user.create.after` hook that\n * auto-provisions a personal organization for every newly-created user\n * (mirroring what SecurityPlugin's middleware does for direct\n * ObjectQL inserts) so SSO-arriving users don't land on the empty\n * \"create organization\" screen.\n */\n databaseHooks?: BetterAuthOptions['databaseHooks'];\n}\n\n/**\n * Authentication Manager\n *\n * Wraps better-auth and provides authentication services for ObjectStack.\n * Supports multiple authentication methods:\n * - Email/password\n * - OAuth providers (Google, GitHub, etc.)\n * - Magic links\n * - Two-factor authentication\n * - Passkeys\n * - Organization/teams\n */\nexport class AuthManager {\n private auth: Auth<any> | null = null;\n private config: AuthManagerOptions;\n\n constructor(config: AuthManagerOptions) {\n this.config = config;\n\n // WebContainer (StackBlitz) compatibility — install a synchronous\n // AsyncLocalStorage polyfill for better-auth's request-state global\n // BEFORE better-auth ever instantiates its own. See the helper for the\n // full rationale.\n installWebContainerRequestStatePolyfill();\n\n // Use provided auth instance\n if (config.authInstance) {\n this.auth = config.authInstance;\n }\n // Don't create auth instance automatically to avoid database initialization errors\n // It will be created lazily when needed\n }\n\n /**\n * Get or create the better-auth instance (lazy initialization)\n */\n private async getOrCreateAuth(): Promise<Auth<any>> {\n if (!this.auth) {\n this.auth = await this.createAuthInstance();\n }\n return this.auth;\n }\n\n /**\n * Create a better-auth instance from configuration\n */\n private async createAuthInstance(): Promise<Auth<any>> {\n const { betterAuth } = await import('better-auth');\n const plugins = await this.buildPluginList();\n const passwordHasher = await this.resolvePasswordHasher();\n const betterAuthConfig: BetterAuthOptions = {\n // Base configuration\n secret: this.config.secret || this.generateSecret(),\n baseURL: this.config.baseUrl || 'http://localhost:3000',\n basePath: this.config.basePath || '/api/v1/auth',\n\n // Database adapter configuration\n database: this.createDatabaseConfig(),\n\n // Model/field mapping: camelCase (better-auth) → snake_case (ObjectStack)\n // These declarations tell better-auth the actual table/column names used\n // by ObjectStack's protocol layer, enabling automatic transformation via\n // createAdapterFactory.\n user: {\n ...AUTH_USER_CONFIG,\n },\n account: {\n ...AUTH_ACCOUNT_CONFIG,\n // Allow OIDC/OAuth callbacks to implicitly link the incoming\n // identity to a pre-existing local user when the emails match.\n //\n // ObjectStack's platform SSO (\"objectstack-cloud\" provider) is the\n // canonical case: cloud is the IdP for every project, so a user\n // arriving via SSO is — by construction — the same person who was\n // auto-seeded as the project owner when the project was created.\n // Without trusting the provider, better-auth's safety check rejects\n // the link with `error=account_not_linked` because the seeded user\n // row has `emailVerified=false` (no actual verification ever runs\n // in the IdP-mediated flow). See packages/plugins/plugin-auth/\n // node_modules/better-auth/dist/oauth2/link-account.mjs:22.\n //\n // Custom-deployment consumers can extend the trusted set via\n // `config.account.accountLinking.trustedProviders`; we always\n // include `objectstack-cloud` because it is the platform IdP.\n accountLinking: {\n enabled: true,\n // better-auth's account-linking gate has TWO independent clauses\n // (see link-account.mjs:22). Trusting the provider only satisfies\n // the first clause; the second — `requireLocalEmailVerified &&\n // !dbUser.user.emailVerified` — still blocks linking when the\n // pre-existing local user row has `emailVerified=false` (the\n // default for owner-seeded rows). Disabling the local-email gate\n // is safe here because the OAuth side is what we actually trust:\n // the incoming identity was verified by the IdP. Consumers who\n // need the stricter behavior can override via config.\n requireLocalEmailVerified: false,\n ...((this.config as any)?.account?.accountLinking ?? {}),\n trustedProviders: Array.from(new Set([\n 'objectstack-cloud',\n ...((this.config as any)?.account?.accountLinking?.trustedProviders ?? []),\n ])),\n },\n },\n verification: {\n ...AUTH_VERIFICATION_CONFIG,\n },\n\n // Social / OAuth providers\n ...(this.config.socialProviders ? { socialProviders: this.config.socialProviders as any } : {}),\n\n // Email and password configuration\n emailAndPassword: {\n enabled: this.config.emailAndPassword?.enabled ?? true,\n ...(passwordHasher ? { password: passwordHasher } : {}),\n ...(this.config.emailAndPassword?.disableSignUp != null\n ? { disableSignUp: this.config.emailAndPassword.disableSignUp } : {}),\n ...(this.config.emailAndPassword?.requireEmailVerification != null\n ? { requireEmailVerification: this.config.emailAndPassword.requireEmailVerification } : {}),\n ...(this.config.emailAndPassword?.minPasswordLength != null\n ? { minPasswordLength: this.config.emailAndPassword.minPasswordLength } : {}),\n ...(this.config.emailAndPassword?.maxPasswordLength != null\n ? { maxPasswordLength: this.config.emailAndPassword.maxPasswordLength } : {}),\n ...(this.config.emailAndPassword?.resetPasswordTokenExpiresIn != null\n ? { resetPasswordTokenExpiresIn: this.config.emailAndPassword.resetPasswordTokenExpiresIn } : {}),\n ...(this.config.emailAndPassword?.autoSignIn != null\n ? { autoSignIn: this.config.emailAndPassword.autoSignIn } : {}),\n ...(this.config.emailAndPassword?.revokeSessionsOnPasswordReset != null\n ? { revokeSessionsOnPasswordReset: this.config.emailAndPassword.revokeSessionsOnPasswordReset } : {}),\n sendResetPassword: async ({ user, url, token }: { user: { id: string; email: string; name?: string }; url: string; token: string }) => {\n const email = this.getEmailService();\n if (!email) {\n console.warn(\n `[AuthManager] Password-reset requested for ${user.email} but no email service is wired. URL: ${url}`,\n );\n return;\n }\n const ttlSec = this.config.emailAndPassword?.resetPasswordTokenExpiresIn ?? 60 * 60;\n try {\n await email.sendTemplate({\n template: 'auth.password_reset',\n to: { address: user.email, ...(user.name ? { name: user.name } : {}) },\n data: {\n user: { name: user.name || user.email, email: user.email, id: user.id },\n resetUrl: url,\n token,\n expiresInMinutes: Math.round(ttlSec / 60),\n appName: this.getAppName(),\n },\n relatedObject: 'sys_user',\n relatedId: user.id,\n });\n } catch (err: any) {\n console.error(`[AuthManager] sendResetPassword failed: ${err?.message ?? err}`);\n throw err;\n }\n },\n },\n\n // Email verification\n ...(this.config.emailVerification || this.config.emailService ? {\n emailVerification: {\n ...(this.config.emailVerification?.sendOnSignUp != null\n ? { sendOnSignUp: this.config.emailVerification.sendOnSignUp } : {}),\n ...(this.config.emailVerification?.sendOnSignIn != null\n ? { sendOnSignIn: this.config.emailVerification.sendOnSignIn } : {}),\n ...(this.config.emailVerification?.autoSignInAfterVerification != null\n ? { autoSignInAfterVerification: this.config.emailVerification.autoSignInAfterVerification } : {}),\n ...(this.config.emailVerification?.expiresIn != null\n ? { expiresIn: this.config.emailVerification.expiresIn } : {}),\n sendVerificationEmail: async ({ user, url, token }: { user: { id: string; email: string; name?: string }; url: string; token: string }) => {\n const email = this.getEmailService();\n if (!email) {\n console.warn(\n `[AuthManager] Verification email requested for ${user.email} but no email service is wired. URL: ${url}`,\n );\n return;\n }\n const ttlSec = this.config.emailVerification?.expiresIn ?? 60 * 60;\n try {\n await email.sendTemplate({\n template: 'auth.verify_email',\n to: { address: user.email, ...(user.name ? { name: user.name } : {}) },\n data: {\n user: { name: user.name || user.email, email: user.email, id: user.id },\n verificationUrl: url,\n token,\n expiresInMinutes: Math.round(ttlSec / 60),\n appName: this.getAppName(),\n },\n relatedObject: 'sys_user',\n relatedId: user.id,\n });\n } catch (err: any) {\n console.error(`[AuthManager] sendVerificationEmail failed: ${err?.message ?? err}`);\n throw err;\n }\n },\n },\n } : {}),\n\n // Session configuration\n session: {\n ...AUTH_SESSION_CONFIG,\n expiresIn: this.config.session?.expiresIn || 60 * 60 * 24 * 7, // 7 days default\n updateAge: this.config.session?.updateAge || 60 * 60 * 24, // 1 day default\n },\n\n // better-auth plugins — registered based on AuthPluginConfig flags\n plugins,\n\n // Database hooks (fired by better-auth's adapter writes — these run\n // for SSO JIT-provisioning too, unlike kernel-level ObjectQL\n // middleware which better-auth's adapter bypasses).\n ...(this.config.databaseHooks ? { databaseHooks: this.config.databaseHooks } : {}),\n\n // Trusted origins for CSRF protection (supports wildcards like \"https://*.example.com\")\n // Auto-includes origins from CORS_ORIGIN env var so CORS and CSRF stay in sync.\n ...(() => {\n const origins: string[] = [...(this.config.trustedOrigins || [])];\n // Sync with CORS_ORIGIN env var (comma-separated)\n const corsOrigin = process.env.CORS_ORIGIN;\n if (corsOrigin && corsOrigin !== '*') {\n corsOrigin.split(',').map(s => s.trim()).filter(Boolean).forEach(o => {\n if (!origins.includes(o)) origins.push(o);\n });\n }\n // When CORS allows all origins (default) and no explicit trustedOrigins,\n // trust all localhost ports in development for convenience. Also trust\n // `*.localhost` subdomains so per-project tenant subdomains (the dev\n // default root domain — see project-provisioning.ts) pass CSRF checks\n // without operators having to configure trustedOrigins manually.\n if (!origins.length && (!corsOrigin || corsOrigin === '*')) {\n origins.push('http://localhost:*');\n origins.push('http://*.localhost:*');\n origins.push('https://*.localhost:*');\n }\n return origins.length ? { trustedOrigins: origins } : {};\n })(),\n\n // Advanced options (cross-subdomain cookies, secure cookies, CSRF, etc.)\n ...(this.config.advanced ? {\n advanced: {\n ...(this.config.advanced.crossSubDomainCookies\n ? { crossSubDomainCookies: this.config.advanced.crossSubDomainCookies } : {}),\n ...(this.config.advanced.useSecureCookies != null\n ? { useSecureCookies: this.config.advanced.useSecureCookies } : {}),\n ...(this.config.advanced.disableCSRFCheck != null\n ? { disableCSRFCheck: this.config.advanced.disableCSRFCheck } : {}),\n ...(this.config.advanced.cookiePrefix != null\n ? { cookiePrefix: this.config.advanced.cookiePrefix } : {}),\n },\n } : {}),\n };\n\n return betterAuth(betterAuthConfig);\n }\n\n /**\n * Detect WebContainer (StackBlitz) and swap in a pure-JS scrypt hasher.\n *\n * better-auth defaults to `@better-auth/utils/password.node`, which calls\n * `node:crypto.scrypt`. WebContainer polyfills that API incompletely and\n * signup throws `TypeError: y.run is not a function`.\n *\n * We can't dynamic-import `@better-auth/utils/password` because that\n * package's `exports` map gates the pure-JS build behind a non-`\"node\"`\n * condition — Node-the-runtime (which WebContainer reports itself as)\n * always resolves to `password.node.mjs`. So we reimplement the same hash\n * here using `@noble/hashes/scrypt` directly, with byte-identical params\n * (N=16384, r=16, p=1, dkLen=64) and the same `{saltHex}:{keyHex}` storage\n * format. Hashes produced by either implementation verify against the\n * other — no migration needed.\n *\n * Returns `undefined` outside WebContainer so production deployments keep\n * the native (fast) hasher and never load `@noble/hashes`.\n */\n private async resolvePasswordHasher(): Promise<\n { hash: (password: string) => Promise<string>; verify: (args: { hash: string; password: string }) => Promise<boolean> } | undefined\n > {\n if (!isWebContainerRuntime()) return undefined;\n try {\n const { scryptAsync } = await import('@noble/hashes/scrypt.js');\n const PARAMS = { N: 16384, r: 16, p: 1, dkLen: 64, maxmem: 128 * 16384 * 16 * 2 } as const;\n const toHex = (b: Uint8Array): string => {\n let s = '';\n for (let i = 0; i < b.length; i++) s += b[i]!.toString(16).padStart(2, '0');\n return s;\n };\n const generateKey = (password: string, saltHex: string): Promise<Uint8Array> =>\n scryptAsync(password.normalize('NFKC'), saltHex, PARAMS);\n return {\n hash: async (password: string) => {\n const saltBytes = (globalThis as any).crypto.getRandomValues(new Uint8Array(16));\n const saltHex = toHex(saltBytes);\n const key = await generateKey(password, saltHex);\n return `${saltHex}:${toHex(key)}`;\n },\n verify: async ({ hash, password }) => {\n const [saltHex, keyHex] = hash.split(':');\n if (!saltHex || !keyHex) throw new Error('Invalid password hash');\n const target = await generateKey(password, saltHex);\n return toHex(target) === keyHex;\n },\n };\n } catch (err: any) {\n console.warn(\n `[AuthManager] WebContainer detected but pure-JS scrypt unavailable: ${err?.message ?? err}. Falling back to default.`,\n );\n return undefined;\n }\n }\n\n /**\n * Build the list of better-auth plugins based on AuthPluginConfig flags.\n *\n * Each plugin that introduces its own database tables is configured with\n * a `schema` option containing the appropriate snake_case field mappings,\n * so that `createAdapterFactory` transforms them automatically.\n */\n private async buildPluginList(): Promise<any[]> {\n const pluginConfig: Partial<AuthPluginConfig> = this.config.plugins ?? {};\n const plugins: any[] = [];\n\n // Defaults — kept in sync with `AuthPluginConfigSchema` in\n // @objectstack/spec/system/auth-config.zod.ts. The frontend AuthProvider\n // (in @object-ui/app-shell) calls `/api/v1/auth/organization/list` on\n // every load; making the org plugin opt-out (default true) avoids\n // 404s and the noisy \"Failed to load organizations\" warning.\n const enabled = {\n organization: pluginConfig.organization ?? true,\n twoFactor: pluginConfig.twoFactor ?? false,\n passkeys: pluginConfig.passkeys ?? false,\n magicLink: pluginConfig.magicLink ?? false,\n oidcProvider: pluginConfig.oidcProvider ?? false,\n deviceAuthorization: pluginConfig.deviceAuthorization ?? false,\n admin: pluginConfig.admin ?? false,\n };\n\n // bearer() — ALWAYS enabled.\n //\n // Enables token-based authentication for cross-origin and mobile clients\n // where third-party cookies are blocked (e.g. Safari ITP, Chrome CHIPS,\n // native apps). The plugin:\n // • Accepts `Authorization: Bearer <token>` on incoming requests and\n // transparently resolves the session as if a cookie had been sent.\n // • Emits a `set-auth-token` response header on sign-in / session-refresh\n // that the client can store (e.g. in `localStorage`) and replay on\n // subsequent requests.\n //\n // This mirrors how Salesforce, Notion, Supabase and first-party mobile\n // SDKs handle auth. Cookie-based auth remains available for same-origin\n // browser deployments; bearer is additive, not a replacement.\n const { bearer } = await import('better-auth/plugins/bearer');\n plugins.push(bearer());\n\n if (enabled.organization) {\n const { organization } = await import('better-auth/plugins/organization');\n // Build a `roles` map that registers each app-supplied org role\n // (e.g. CRM's sales_rep, sales_manager) as a valid Better-Auth role\n // so invitations to those roles aren't rejected with ROLE_NOT_FOUND.\n // Real RBAC enforcement is handled by ObjectStack's SecurityPlugin,\n // which matches the role name against `permission` metadata\n // (PermissionSets). Here we register them with minimum org-plugin\n // capabilities (same as the built-in `member` role) so they cannot\n // inadvertently grant org-level admin powers.\n let customOrgRoles: Record<string, any> | undefined;\n const extra = this.config.additionalOrgRoles;\n if (extra && extra.length > 0) {\n try {\n const accessMod = await import('better-auth/plugins/organization/access');\n const { defaultAc, memberAc, defaultRoles: importedDefaultRoles } = accessMod as any;\n // Better-Auth's `hasPermission` does `{...options.roles || defaultRoles}`\n // (precedence: `||` then spread). When we pass our own `roles`, the\n // built-in owner/admin/member are silently dropped, so even the org\n // owner loses `invitation:create` and every mutation 403s. We must\n // re-include the defaults alongside our extras.\n const defaultRoles = importedDefaultRoles || null;\n if (defaultAc && memberAc && typeof memberAc.statements === 'object') {\n const built: Record<string, any> = defaultRoles ? { ...defaultRoles } : {};\n const stmts = memberAc.statements;\n for (const name of extra) {\n if (!name) continue;\n if (built[name]) continue;\n built[name] = defaultAc.newRole(stmts);\n }\n customOrgRoles = built;\n }\n } catch {\n customOrgRoles = undefined;\n }\n }\n plugins.push(organization({\n schema: buildOrganizationPluginSchema(),\n // Enable the team sub-feature so the framework's `sys_team` /\n // `sys_team_member` tables (already declared in platform-objects)\n // are actually wired up to better-auth's CRUD endpoints\n // (`/organization/{create,update,remove,list}-team[s]` and\n // `/organization/{add,remove,list}-team-member[s]`). The Account\n // portal exposes a Teams page; without this flag those endpoints\n // 404 and the section silently breaks.\n teams: { enabled: true },\n // Without a mailer wired in framework, requiring email verification\n // before accepting invitations dead-ends every invite flow with\n // FORBIDDEN EMAIL_VERIFICATION_REQUIRED…. Default-off here keeps\n // the built-in /accept-invitation route usable for pilots; operators\n // who wire a real mailer can re-enable downstream.\n requireEmailVerificationOnInvitation: false,\n ...(customOrgRoles ? { roles: customOrgRoles } : {}),\n // ── Slug-change guard ─────────────────────────────────────\n // An org's slug is baked into every env hostname at creation\n // time (see service-tenant `project-provisioning.ts`). Renaming\n // it while live envs exist would silently desync the URL from\n // the org identity. Block the change here; the cloud Console\n // surfaces this as an actionable error and points users to\n // `change_hostname` or archiving the env. Org `name` (display\n // label) is unaffected — only `slug` is guarded.\n //\n // We resolve the data engine lazily so non-cloud apps (which\n // never seed `sys_environment`) keep working: any lookup error\n // is treated as \"no envs to protect\".\n organizationHooks: {\n beforeUpdateOrganization: async ({ organization, member }: any) => {\n const newSlug = organization?.slug;\n const orgId = member?.organizationId;\n if (!newSlug || !orgId) return;\n\n const dataEngine = this.config.dataEngine as any;\n if (!dataEngine) return;\n\n let currentSlug: string | undefined;\n try {\n const current = await dataEngine.findOne('sys_organization', {\n where: { id: orgId },\n });\n currentSlug = current?.slug;\n } catch {\n return;\n }\n if (!currentSlug || currentSlug === newSlug) return;\n\n let activeEnvs = 0;\n try {\n const envs = await dataEngine.find('sys_environment', {\n where: { organization_id: orgId },\n });\n activeEnvs = (envs ?? []).filter(\n (e: any) => e?.status !== 'archived' && e?.status !== 'failed',\n ).length;\n } catch {\n return;\n }\n\n if (activeEnvs > 0) {\n const { APIError } = await import('better-auth/api');\n throw new APIError('FORBIDDEN', {\n message:\n `Cannot change organization slug while ${activeEnvs} active ` +\n `environment(s) still reference it. Archive those environments ` +\n `or rename their hostnames first.`,\n });\n }\n },\n },\n // No mailer is wired in framework yet — log the accept URL so\n // operators / UI can fall back to copy-paste flows. Replace this\n // with a real mail integration when available.\n sendInvitationEmail: async ({ email: recipientEmail, invitation, organization: org, inviter }) => {\n const baseUrl = (this.config.baseUrl ?? '').replace(/\\/$/, '');\n const acceptUrl = `${baseUrl}/accept-invitation/${invitation.id}`;\n const emailService = this.getEmailService();\n if (!emailService) {\n console.warn(\n `[AuthManager] Invitation email not configured. ` +\n `To: ${recipientEmail} (org: ${org?.name ?? invitation.organizationId}, ` +\n `role: ${invitation.role}, inviter: ${inviter?.user?.email ?? 'unknown'}) ` +\n `URL: ${acceptUrl}`,\n );\n return;\n }\n try {\n await emailService.sendTemplate({\n template: 'auth.invitation',\n to: recipientEmail,\n data: {\n inviter: {\n name: inviter?.user?.name ?? inviter?.user?.email ?? 'A teammate',\n email: inviter?.user?.email ?? '',\n },\n organization: { name: org?.name ?? invitation.organizationId },\n role: invitation.role || '',\n acceptUrl,\n appName: this.getAppName(),\n },\n relatedObject: 'sys_invitation',\n relatedId: invitation.id,\n });\n } catch (err: any) {\n console.error(`[AuthManager] sendInvitationEmail failed: ${err?.message ?? err}`);\n throw err;\n }\n },\n }));\n }\n\n if (enabled.twoFactor) {\n const { twoFactor } = await import('better-auth/plugins/two-factor');\n plugins.push(twoFactor({\n schema: buildTwoFactorPluginSchema(),\n }));\n }\n\n if (enabled.admin) {\n const { admin } = await import('better-auth/plugins/admin');\n // Platform admin: ban/unban, set-password, impersonate, set-role.\n // Schema mapping ensures the plugin's added user/session columns\n // match ObjectStack's snake_case conventions (ban_reason,\n // ban_expires, impersonated_by). `role` and `banned` are already\n // snake_case-compatible.\n plugins.push(admin({\n schema: buildAdminPluginSchema(),\n }));\n }\n\n if (enabled.magicLink) {\n const { magicLink } = await import('better-auth/plugins/magic-link');\n // magic-link reuses the `verification` table — no extra schema mapping needed.\n plugins.push(magicLink({\n sendMagicLink: async ({ email: recipientEmail, url, token }) => {\n const emailService = this.getEmailService();\n if (!emailService) {\n console.warn(\n `[AuthManager] Magic-link requested for ${recipientEmail} but no email service is wired. URL: ${url}`,\n );\n return;\n }\n try {\n await emailService.sendTemplate({\n template: 'auth.magic_link',\n to: recipientEmail,\n data: {\n magicLinkUrl: url,\n token,\n expiresInMinutes: 10,\n appName: this.getAppName(),\n },\n });\n } catch (err: any) {\n console.error(`[AuthManager] sendMagicLink failed: ${err?.message ?? err}`);\n throw err;\n }\n },\n }));\n }\n\n // OIDC / Generic OAuth2 providers (enterprise SSO via genericOAuth plugin)\n if (this.config.oidcProviders?.length) {\n const { genericOAuth } = await import('better-auth/plugins/generic-oauth');\n plugins.push(genericOAuth({\n config: this.config.oidcProviders.map(p => ({\n providerId: p.providerId,\n ...(p.discoveryUrl ? { discoveryUrl: p.discoveryUrl } : {}),\n ...(p.issuer ? { issuer: p.issuer } : {}),\n ...(p.authorizationUrl ? { authorizationUrl: p.authorizationUrl } : {}),\n ...(p.tokenUrl ? { tokenUrl: p.tokenUrl } : {}),\n ...(p.userInfoUrl ? { userInfoUrl: p.userInfoUrl } : {}),\n clientId: p.clientId,\n clientSecret: p.clientSecret,\n ...(p.scopes ? { scopes: p.scopes } : {}),\n ...(p.pkce != null ? { pkce: p.pkce } : {}),\n })),\n }));\n }\n\n // OAuth/OIDC Provider — turn this server into an OpenID Connect Identity\n // Provider so external apps can SSO via ObjectStack. Adds the\n // `/oauth2/{authorize,token,userinfo,register,consent,endsession}` and\n // `/.well-known/openid-configuration` endpoints under the auth route.\n //\n // Migrated from the deprecated `better-auth/plugins/oidc-provider` to the\n // standalone `@better-auth/oauth-provider` package. The new plugin uses\n // `oauthClient`, `oauthAccessToken`, `oauthRefreshToken`, and `oauthConsent`\n // models — see `buildOauthProviderPluginSchema()` for the snake_case\n // mappings to ObjectStack's `sys_oauth_*` tables.\n if (enabled.oidcProvider) {\n // The new @better-auth/oauth-provider package requires the `jwt`\n // plugin (used to sign id_tokens / JWT access tokens). Register it\n // automatically — it is otherwise an internal implementation detail\n // and forcing every consumer to opt in would be poor DX.\n const { jwt } = await import('better-auth/plugins');\n plugins.push(jwt({ schema: buildJwtPluginSchema() }));\n\n const { oauthProvider } = await import('@better-auth/oauth-provider');\n const baseUrl = (this.config.baseUrl ?? '').replace(/\\/$/, '');\n plugins.push(oauthProvider({\n // Account SPA renders both pages — see apps/account.\n loginPage: `${baseUrl}/_account/login`,\n consentPage: `${baseUrl}/_account/oauth/consent`,\n schema: buildOauthProviderPluginSchema(),\n }));\n }\n\n // Device Authorization Grant (RFC 8628) — for CLI / TV-style devices.\n // Exposes the standard `/device/{code,token,approve,deny}` endpoints\n // and persists pending requests in `sys_device_code`.\n //\n // The verification URI points at the account portal page that lets a\n // signed-in user approve or deny a pending CLI login. The page reads\n // the `user_code` query parameter that better-auth appends to\n // `verification_uri_complete`.\n if (enabled.deviceAuthorization) {\n const { deviceAuthorization } = await import('better-auth/plugins/device-authorization');\n const baseUrl = (this.config.baseUrl ?? '').replace(/\\/$/, '');\n plugins.push(deviceAuthorization({\n verificationUri: `${baseUrl}/_account/auth/device`,\n schema: buildDeviceAuthorizationPluginSchema(),\n }));\n }\n\n // customSession() — augments the session payload with a derived `role`\n // field so frontend gating (e.g. AppShell's `isAdmin = user.role === 'admin'`)\n // works without each consumer having to re-query permission sets.\n //\n // Better-auth's `sys_user` table doesn't carry a `role` column. We derive\n // it from two sources:\n //\n // 1. **Platform admin** — a `sys_user_permission_set` row that points at\n // the `admin_full_access` permission set with `organization_id = null`\n // (seeded by `bootstrapPlatformAdmin`).\n // 2. **Organization admin** — a `sys_member` row in the user's *active*\n // organization (`session.activeOrganizationId`) with role `owner` or\n // `admin`. Org owners/admins are entitled to manage org-scoped\n // metadata such as saved list views, dashboards, etc.\n //\n // Either path synthesizes `user.role = 'admin'` so the frontend can gate\n // metadata-edit affordances uniformly. The raw membership role remains\n // available via the `organization` plugin's `member` payload for\n // finer-grained checks.\n const dataEngine = this.config.dataEngine;\n if (dataEngine) {\n const { customSession } = await import('better-auth/plugins/custom-session');\n plugins.push(customSession(async ({ user, session }) => {\n if (!user?.id) return { user, session };\n\n const isPlatformAdmin = async (): Promise<boolean> => {\n try {\n const links = await dataEngine.find('sys_user_permission_set', {\n where: { user_id: user.id },\n limit: 50,\n });\n const platformLinks = (Array.isArray(links) ? links : []).filter(\n (l: any) => !l.organization_id,\n );\n if (platformLinks.length === 0) return false;\n const sets = await dataEngine.find('sys_permission_set', { limit: 50 });\n const adminSet = (Array.isArray(sets) ? sets : []).find(\n (r: any) => r.name === 'admin_full_access',\n );\n if (!adminSet) return false;\n return platformLinks.some(\n (l: any) => l.permission_set_id === adminSet.id,\n );\n } catch {\n return false;\n }\n };\n\n const isActiveOrgAdmin = async (): Promise<boolean> => {\n try {\n const orgId = (session as any)?.activeOrganizationId;\n if (!orgId) return false;\n const members = await dataEngine.find('sys_member', {\n where: { user_id: user.id, organization_id: orgId },\n limit: 5,\n });\n return (Array.isArray(members) ? members : []).some((m: any) => {\n // better-auth org plugin stores roles as either a single string\n // or a comma-separated list (e.g. `\"owner,admin\"`). Treat any\n // membership that includes `owner` or `admin` as administrative.\n const raw = typeof m?.role === 'string' ? m.role : '';\n const roles = raw.split(',').map((s: string) => s.trim().toLowerCase());\n return roles.includes('owner') || roles.includes('admin');\n });\n } catch {\n return false;\n }\n };\n\n const promote = (await isPlatformAdmin()) || (await isActiveOrgAdmin());\n if (!promote) return { user, session };\n return { user: { ...user, role: 'admin' }, session };\n }));\n }\n\n return plugins;\n }\n\n /**\n * Create database configuration using ObjectQL adapter\n *\n * better-auth resolves the `database` option as follows:\n * - `undefined` → in-memory adapter\n * - `typeof fn === \"function\"` → treated as `DBAdapterInstance`, called with `(options)`\n * - otherwise → forwarded to Kysely adapter factory (pool/dialect)\n *\n * A raw `CustomAdapter` object would fall into the third branch and fail\n * silently. We therefore wrap the ObjectQL adapter in a factory function\n * so it is correctly recognised as a `DBAdapterInstance`.\n */\n private createDatabaseConfig(): any {\n // Use ObjectQL adapter factory if dataEngine is provided\n if (this.config.dataEngine) {\n // createObjectQLAdapterFactory returns an AdapterFactory\n // (options => DBAdapter) which better-auth invokes via getBaseAdapter().\n // The factory is created by better-auth's createAdapterFactory and\n // automatically applies modelName/fields transformations declared in\n // the betterAuth config above.\n return createObjectQLAdapterFactory(this.config.dataEngine);\n }\n\n // Fallback warning if no dataEngine is provided\n console.warn(\n '⚠️ WARNING: No dataEngine provided to AuthManager! ' +\n 'Using in-memory storage. This is NOT suitable for production. ' +\n 'Please provide a dataEngine instance (e.g., ObjectQL) in AuthManagerOptions.'\n );\n\n // Return a minimal in-memory configuration as fallback\n // This allows the system to work in development/testing without a real database\n return undefined; // better-auth will use its default in-memory adapter\n }\n\n /**\n * Generate a secure secret if not provided\n */\n private generateSecret(): string {\n const envSecret = process.env.AUTH_SECRET;\n\n if (!envSecret) {\n // In production, a secret MUST be provided\n // For development/testing, we'll use a fallback but warn about it\n const fallbackSecret = 'dev-secret-' + Date.now();\n\n console.warn(\n '⚠️ WARNING: No AUTH_SECRET environment variable set! ' +\n 'Using a temporary development secret. ' +\n 'This is NOT secure for production use. ' +\n 'Please set AUTH_SECRET in your environment variables.'\n );\n\n return fallbackSecret;\n }\n\n return envSecret;\n }\n\n /**\n * Update the base URL at runtime.\n *\n * This **must** be called before the first request triggers lazy\n * initialisation of the better-auth instance — typically from a\n * `kernel:ready` hook where the actual server port is known.\n *\n * If the auth instance has already been created this is a no-op and\n * a warning is emitted.\n */\n setRuntimeBaseUrl(url: string): void {\n if (this.auth) {\n console.warn(\n '[AuthManager] setRuntimeBaseUrl() called after the auth instance was already created — ignoring. ' +\n 'Ensure this method is called before the first request.',\n );\n return;\n }\n this.config = { ...this.config, baseUrl: url };\n }\n\n /**\n * Inject (or replace) the outbound email service used by better-auth\n * callbacks. Safe to call after construction but BEFORE the first\n * request hits the auth handler — callbacks read this via\n * {@link getEmailService} when invoked.\n *\n * AuthPlugin calls this on `kernel:ready` once `ctx.getService('email')`\n * resolves. For tests / serverless, callers may invoke directly.\n */\n setEmailService(email: IEmailService | undefined): void {\n this.config.emailService = email;\n }\n\n /** @internal Used by callback closures. */\n private getEmailService(): IEmailService | undefined {\n return this.config.emailService;\n }\n\n /** @internal `{{appName}}` placeholder value for built-in templates. */\n private getAppName(): string {\n return this.config.appName ?? 'ObjectStack';\n }\n\n /**\n * Get the underlying better-auth instance\n * Useful for advanced use cases\n */\n getAuthInstance(): Promise<Auth<any>> {\n return this.getOrCreateAuth();\n }\n\n /**\n * Handle an authentication request\n * Forwards the request directly to better-auth's universal handler\n *\n * better-auth catches internal errors (database / adapter / ORM) and\n * returns a 500 Response instead of throwing. We therefore inspect the\n * response status and log server errors so they are not silently swallowed.\n *\n * @param request - Web standard Request object\n * @returns Web standard Response object\n */\n async handleRequest(request: Request): Promise<Response> {\n const auth = await this.getOrCreateAuth();\n // better-auth's HTTP entrypoint (`createBetterAuth.handler`) wraps execution\n // in `runWithAdapter` but NOT `runWithRequestState`. Endpoints that read\n // request-state via `defineRequestState()` (e.g. `should-session-refresh`,\n // `oauth`) therefore throw \"No request state found\" when reached via HTTP.\n // The `customSession` plugin triggers this by invoking the inner\n // `getSession()` endpoint directly, bypassing `to-auth-endpoints`'\n // auto-wrap. We establish the ALS store here so all downstream endpoint\n // calls inherit a valid request-state WeakMap.\n const { runWithRequestState } = await import('@better-auth/core/context');\n const response = await runWithRequestState(new WeakMap(), () => auth.handler(request));\n\n if (response.status >= 500) {\n try {\n const body = await response.clone().text();\n console.error('[AuthManager] better-auth returned error:', response.status, body);\n } catch {\n console.error('[AuthManager] better-auth returned error:', response.status, '(unable to read body)');\n }\n }\n\n return response;\n }\n\n /**\n * Get the better-auth API for programmatic access\n * Use this for server-side operations (e.g., creating users, checking sessions)\n */\n async getApi(): Promise<Auth<any>['api']> {\n const auth = await this.getOrCreateAuth();\n return auth.api;\n }\n\n /**\n * Get the underlying better-auth context for low-level operations such as\n * `internalAdapter.createAccount` / `password.hash`.\n *\n * Used by routes that need to write to better-auth's tables outside the\n * normal endpoint surface — currently only `set-initial-password`, which\n * provisions a credential account for SSO-onboarded users so they can\n * sign in with email/password going forward.\n */\n async getAuthContext(): Promise<any> {\n const auth = await this.getOrCreateAuth();\n return (auth as any).$context;\n }\n\n // ---------------------------------------------------------------------------\n // Device Flow (CLI browser-based login)\n //\n // The device authorization flow (RFC 8628) is now handled entirely by\n // better-auth's `device-authorization` plugin. Endpoints are exposed at\n // `${basePath}/device/{code,token,approve,deny}` and persisted in\n // `sys_device_code`. Enable via `plugins.deviceAuthorization: true` in\n // AuthPluginConfig.\n // ---------------------------------------------------------------------------\n\n getPublicConfig() {\n // Extract social providers info (without sensitive data)\n const socialProviders = [];\n if (this.config.socialProviders) {\n for (const [id, providerConfig] of Object.entries(this.config.socialProviders)) {\n if (providerConfig.enabled !== false) {\n // Map provider ID to friendly name\n const nameMap: Record<string, string> = {\n google: 'Google',\n github: 'GitHub',\n microsoft: 'Microsoft',\n apple: 'Apple',\n facebook: 'Facebook',\n twitter: 'Twitter',\n discord: 'Discord',\n gitlab: 'GitLab',\n linkedin: 'LinkedIn',\n };\n\n socialProviders.push({\n id,\n name: nameMap[id] || id.charAt(0).toUpperCase() + id.slice(1),\n enabled: true,\n type: 'social' as const,\n });\n }\n }\n }\n\n // Append OIDC providers\n if (this.config.oidcProviders?.length) {\n for (const p of this.config.oidcProviders) {\n socialProviders.push({\n id: p.providerId,\n name: p.name ?? (p.providerId.charAt(0).toUpperCase() + p.providerId.slice(1)),\n enabled: true,\n type: 'oidc' as const,\n });\n }\n }\n\n // Extract email/password config (safe fields only)\n const emailPasswordConfig: Partial<EmailAndPasswordConfig> = this.config.emailAndPassword ?? {};\n const emailPassword = {\n enabled: emailPasswordConfig.enabled !== false, // Default to true\n disableSignUp: emailPasswordConfig.disableSignUp ?? false,\n requireEmailVerification: emailPasswordConfig.requireEmailVerification ?? false,\n };\n\n // Extract enabled features\n const pluginConfig: Partial<AuthPluginConfig> = this.config.plugins ?? {};\n const features = {\n twoFactor: pluginConfig.twoFactor ?? false,\n passkeys: pluginConfig.passkeys ?? false,\n magicLink: pluginConfig.magicLink ?? false,\n organization: pluginConfig.organization ?? true,\n oidcProvider: pluginConfig.oidcProvider ?? false,\n deviceAuthorization: pluginConfig.deviceAuthorization ?? false,\n };\n\n return {\n emailPassword,\n socialProviders,\n features,\n };\n }\n\n /**\n * Returns the data engine wired into this auth manager. Used by route\n * handlers (e.g. bootstrap-status) that need to query identity tables\n * directly without going through better-auth.\n */\n public getDataEngine(): IDataEngine | undefined {\n return this.config.dataEngine;\n }\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * Canonical plugin-auth manifest source.\n *\n * Both `objectstack.config.ts` (compile-time) and `auth-plugin.ts`\n * (runtime `manifest.register`) import from this file so the two\n * registration paths cannot drift (D7).\n */\n\nimport {\n SysAccount,\n SysApiKey,\n SysDeviceCode,\n SysInvitation,\n SysMember,\n SysJwks,\n SysOauthAccessToken,\n SysOauthApplication,\n SysOauthConsent,\n SysOauthRefreshToken,\n SysOrganization,\n SysSession,\n SysTeam,\n SysTeamMember,\n SysTwoFactor,\n SysUser,\n SysUserPreference,\n SysVerification,\n} from '@objectstack/platform-objects/identity';\n\nexport const AUTH_PLUGIN_ID = 'com.objectstack.plugin-auth';\nexport const AUTH_PLUGIN_VERSION = '3.0.1';\n\n/** Identity objects owned by plugin-auth. */\nexport const authIdentityObjects: any[] = [\n SysUser,\n SysSession,\n SysAccount,\n SysVerification,\n SysOrganization,\n SysMember,\n SysInvitation,\n SysTeam,\n SysTeamMember,\n SysApiKey,\n SysTwoFactor,\n SysUserPreference,\n SysOauthApplication,\n SysOauthAccessToken,\n SysOauthRefreshToken,\n SysOauthConsent,\n SysJwks,\n SysDeviceCode,\n];\n\n/** Manifest header shared by compile-time config and runtime registration. */\nexport const authPluginManifestHeader = {\n id: AUTH_PLUGIN_ID,\n namespace: 'sys',\n version: AUTH_PLUGIN_VERSION,\n type: 'plugin' as const,\n scope: 'system' as const,\n defaultDatasource: 'cloud',\n name: 'Authentication & Identity Plugin',\n description: 'Core authentication objects for ObjectStack (User, Session, Account, Verification)',\n};\n"],"mappings":"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","names":["import_system","organization","dataEngine"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/auth-plugin.ts","../src/objectql-adapter.ts","../src/auth-schema-config.ts","../src/auth-manager.ts","../src/manifest.ts"],"sourcesContent":["// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * @objectstack/plugin-auth\n * \n * Authentication & Identity Plugin for ObjectStack\n * Powered by better-auth for robust, secure authentication\n * Uses ObjectQL for data persistence (no third-party ORM required)\n */\n\nexport * from './auth-plugin.js';\nexport * from './auth-manager.js';\nexport * from './objectql-adapter.js';\nexport * from './auth-schema-config.js';\nexport type { AuthConfig, AuthProviderConfig, AuthPluginConfig } from '@objectstack/spec/system';\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport { Plugin, PluginContext, IHttpServer } from '@objectstack/core';\nimport type { BetterAuthOptions } from 'better-auth';\nimport { AuthConfig } from '@objectstack/spec/system';\nimport {\n SETUP_APP,\n SystemOverviewDashboard,\n SetupAppTranslations,\n} from '@objectstack/platform-objects/apps';\nimport { AuthManager } from './auth-manager.js';\nimport {\n authIdentityObjects,\n authPluginManifestHeader,\n} from './manifest.js';\n\n/**\n * Auth Plugin Options\n * Extends AuthConfig from spec with additional runtime options\n */\nexport interface AuthPluginOptions extends Partial<AuthConfig> {\n /**\n * Whether to automatically register auth routes\n * @default true\n */\n registerRoutes?: boolean;\n \n /**\n * Base path for auth routes\n * @default '/api/v1/auth'\n */\n basePath?: string;\n\n /**\n * Override the datasource that owns the identity tables (sys_user,\n * sys_session, …) when AuthPlugin's manifest is registered.\n *\n * Defaults to `'cloud'` (control-plane DB) so the historical\n * single-tenant control-plane behaviour is preserved. Per-project\n * kernels in objectos pass `'default'` so identity tables live in the\n * project's own database — each project owns its own users.\n */\n manifestDatasource?: string;\n\n /**\n * Application-specific organization roles to register with Better-Auth's\n * organization plugin so invitations to those roles aren't rejected with\n * ROLE_NOT_FOUND. Forwarded as-is to AuthManager. See\n * {@link AuthManagerOptions.additionalOrgRoles} for details.\n */\n additionalOrgRoles?: string[];\n\n /**\n * Pass-through to better-auth's `databaseHooks` option. Used by\n * platform consumers (objectos kernel) to attach a\n * `user.create.after` hook that auto-provisions a personal\n * organization for JIT-created SSO users — better-auth's adapter\n * bypasses kernel-level ObjectQL middleware, so this is the only\n * hook point that fires for every user creation path (email signup,\n * social/OIDC sign-in, admin-created accounts).\n */\n databaseHooks?: BetterAuthOptions['databaseHooks'];\n}\n\n/**\n * Authentication Plugin\n * \n * Provides authentication and identity services for ObjectStack applications.\n * \n * **Dual-Mode Operation:**\n * - **Server mode** (HonoServerPlugin active): Registers HTTP routes at basePath,\n * forwarding all auth requests to better-auth's universal handler.\n * - **MSW/Mock mode** (no HTTP server): Gracefully skips route registration but\n * still registers the `auth` service, allowing HttpDispatcher.handleAuth() to\n * simulate auth flows (sign-up, sign-in, etc.) for development and testing.\n * \n * Features:\n * - Session management\n * - User registration/login\n * - OAuth providers (Google, GitHub, etc.)\n * - Organization/team support\n * - 2FA, passkeys, magic links\n * \n * This plugin registers:\n * - `auth` service (auth manager instance) — always\n * - `app.com.objectstack.system` service (system object definitions) — always\n * - HTTP routes for authentication endpoints — only when HTTP server is available\n * \n * Integrates with better-auth library to provide comprehensive\n * authentication capabilities including email/password, OAuth, 2FA,\n * magic links, passkeys, and organization support.\n */\nexport class AuthPlugin implements Plugin {\n name = 'com.objectstack.auth';\n type = 'standard';\n version = '1.0.0';\n dependencies: string[] = ['com.objectstack.engine.objectql']; // manifest service required\n \n private options: AuthPluginOptions;\n private authManager: AuthManager | null = null;\n\n constructor(options: AuthPluginOptions = {}) {\n this.options = {\n registerRoutes: true,\n basePath: '/api/v1/auth',\n ...options\n };\n }\n\n async init(ctx: PluginContext): Promise<void> {\n ctx.logger.info('Initializing Auth Plugin...');\n\n // Validate required configuration\n if (!this.options.secret) {\n throw new Error('AuthPlugin: secret is required');\n }\n\n // Get data engine service for database operations\n const dataEngine = ctx.getService<any>('data');\n if (!dataEngine) {\n ctx.logger.warn('No data engine service found - auth will use in-memory storage');\n }\n\n // Initialize auth manager with data engine\n this.authManager = new AuthManager({\n ...this.options,\n dataEngine,\n });\n\n // Register auth service\n ctx.registerService('auth', this.authManager);\n\n ctx.getService<{ register(m: any): void }>('manifest').register({\n ...authPluginManifestHeader,\n ...(this.options.manifestDatasource\n ? { defaultDatasource: this.options.manifestDatasource }\n : {}),\n objects: authIdentityObjects,\n // The platform Setup App is a static metadata artifact (lives in\n // @objectstack/platform-objects/apps). plugin-auth is the natural\n // owner of its registration since it loads first among the trio\n // (auth + security + audit) that supplies the underlying objects.\n apps: [SETUP_APP],\n // List views for each Setup-nav object are defined on the schema\n // itself via the canonical `listViews` map (e.g.\n // sys_user.listViews.{all_users,unverified,two_factor}). Registering\n // top-level views here is the legacy pre-M10.30c pattern — it caused\n // duplicate \"Users\"/\"Roles\"/\"Sessions\" tabs to appear alongside the\n // schema-derived ones, sometimes referencing nonexistent fields\n // (e.g. legacy `users.view` had phone/status/active columns that do\n // not exist on sys_user). Schema-embedded listViews is the single\n // source of truth.\n dashboards: [SystemOverviewDashboard],\n });\n\n ctx.logger.info('Auth Plugin initialized successfully');\n }\n\n async start(ctx: PluginContext): Promise<void> {\n ctx.logger.info('Starting Auth Plugin...');\n\n if (!this.authManager) {\n throw new Error('Auth manager not initialized');\n }\n\n // Contribute the static Setup App translation bundle into the kernel's\n // i18n service. Done in `kernel:ready` (not `init`) because the i18n\n // service plugin is typically registered AFTER capability-loaded service\n // plugins. Mirrors the pattern used by SettingsServicePlugin for its\n // built-in settings translations. Gracefully skips when no i18n service\n // is registered (e.g. lean test/MSW kernels) — Setup App labels then\n // fall back to the inline literals on the App / Dashboard metadata.\n ctx.hook('kernel:ready', async () => {\n try {\n const i18n = ctx.getService<{\n loadTranslations: (locale: string, data: Record<string, unknown>) => void;\n }>('i18n');\n let loaded = 0;\n for (const [locale, data] of Object.entries(SetupAppTranslations)) {\n if (data && typeof data === 'object') {\n try {\n i18n.loadTranslations(locale, data as Record<string, unknown>);\n loaded++;\n } catch (err: any) {\n ctx.logger.warn(\n `Auth: failed to load Setup App translations for '${locale}': ${err?.message ?? err}`,\n );\n }\n }\n }\n if (loaded > 0) {\n ctx.logger.info(\n `Auth: contributed Setup App translations (${loaded} locale${loaded > 1 ? 's' : ''})`,\n );\n }\n } catch {\n // i18n service not registered — Setup App falls back to inline labels.\n }\n });\n\n // Defer HTTP route registration to kernel:ready hook.\n // This ensures all plugins (including HonoServerPlugin) have completed\n // their init and start phases before we attempt to look up the\n // http-server service — making AuthPlugin resilient to plugin\n // loading order.\n if (this.options.registerRoutes) {\n ctx.hook('kernel:ready', async () => {\n // Inject the email service if available so better-auth callbacks\n // (sendResetPassword / sendVerificationEmail / sendInvitationEmail\n // / sendMagicLink) can actually deliver mail. Resolved here on\n // kernel:ready so EmailServicePlugin has had a chance to register.\n if (this.authManager) {\n try {\n const emailSvc = ctx.getService<any>('email');\n if (emailSvc) {\n this.authManager.setEmailService(emailSvc);\n ctx.logger.info('Auth: email service wired (transactional mail enabled)');\n }\n } catch {\n ctx.logger.info('Auth: no email service registered — auth callbacks will log instead of sending');\n }\n }\n\n let httpServer: IHttpServer | null = null;\n try {\n httpServer = ctx.getService<IHttpServer>('http-server');\n } catch {\n // Service not found — expected in MSW/mock mode\n }\n\n if (httpServer) {\n // Auto-detect the actual server URL when no explicit baseUrl was\n // configured, or when the configured baseUrl uses a different port\n // than the running server (e.g. port 3000 configured but 3002 bound).\n // getPort() is optional on IHttpServer; duck-type check for it.\n const serverWithPort = httpServer as IHttpServer & { getPort?: () => number };\n if (this.authManager && typeof serverWithPort.getPort === 'function') {\n const actualPort = serverWithPort.getPort();\n if (actualPort) {\n const configuredUrl = this.options.baseUrl || 'http://localhost:3000';\n const configuredOrigin = new URL(configuredUrl).origin;\n const actualUrl = `http://localhost:${actualPort}`;\n\n // Only auto-correct the port when the configured URL is already a\n // localhost URL (development mode). In production (Vercel/cloud) the\n // configured baseUrl is the real public hostname — never overwrite it\n // with a localhost URL, which would break OAuth callback URLs.\n const configuredIsLocalhost = configuredOrigin.startsWith('http://localhost');\n if (configuredIsLocalhost && configuredOrigin !== actualUrl) {\n this.authManager.setRuntimeBaseUrl(actualUrl);\n ctx.logger.info(\n `Auth baseUrl auto-updated to ${actualUrl} (configured: ${configuredUrl})`,\n );\n }\n }\n }\n\n // Route registration errors should propagate (server misconfiguration)\n this.registerAuthRoutes(httpServer, ctx);\n ctx.logger.info(`Auth routes registered at ${this.options.basePath}`);\n } else {\n ctx.logger.warn(\n 'No HTTP server available — auth routes not registered. ' +\n 'Auth service is still available for MSW/mock environments via HttpDispatcher.'\n );\n }\n });\n }\n\n // Register auth middleware on ObjectQL engine (if available)\n try {\n const ql = ctx.getService<any>('objectql');\n if (ql && typeof ql.registerMiddleware === 'function') {\n ql.registerMiddleware(async (opCtx: any, next: () => Promise<void>) => {\n // If context already has userId or isSystem, skip auth resolution\n if (opCtx.context?.userId || opCtx.context?.isSystem) {\n return next();\n }\n // Future: resolve session from AsyncLocalStorage or request context\n await next();\n });\n ctx.logger.info('Auth middleware registered on ObjectQL engine');\n }\n } catch (_e) {\n ctx.logger.debug('ObjectQL engine not available, skipping auth middleware registration');\n }\n\n ctx.logger.info('Auth Plugin started successfully');\n }\n\n async destroy(): Promise<void> {\n // Cleanup if needed\n this.authManager = null;\n }\n\n /**\n * Register authentication routes with HTTP server\n * \n * Uses better-auth's universal handler for all authentication requests.\n * This forwards all requests under basePath to better-auth, which handles:\n * - Email/password authentication\n * - OAuth providers (Google, GitHub, etc.)\n * - Session management\n * - Password reset\n * - Email verification\n * - 2FA, passkeys, magic links (if enabled)\n */\n private registerAuthRoutes(httpServer: IHttpServer, ctx: PluginContext): void {\n if (!this.authManager) return;\n\n const basePath = this.options.basePath || '/api/v1/auth';\n\n // Get raw Hono app to use native wildcard routing\n // Type assertion is safe here because we explicitly require Hono server as a dependency\n if (!('getRawApp' in httpServer) || typeof (httpServer as any).getRawApp !== 'function') {\n ctx.logger.error('HTTP server does not support getRawApp() - wildcard routing requires Hono server');\n throw new Error(\n 'AuthPlugin requires HonoServerPlugin for wildcard routing support. ' +\n 'Please ensure HonoServerPlugin is loaded before AuthPlugin.'\n );\n }\n\n const rawApp = (httpServer as any).getRawApp();\n\n // Register /config before the wildcard so it takes precedence.\n // better-auth has no /config endpoint, so without this explicit route\n // the wildcard below forwards the request and better-auth returns 404.\n rawApp.get(`${basePath}/config`, (c: any) => {\n try {\n const config = this.authManager!.getPublicConfig();\n return c.json({ success: true, data: config });\n } catch (error) {\n const err = error instanceof Error ? error : new Error(String(error));\n return c.json({ success: false, error: { code: 'auth_config_error', message: err.message } }, 500);\n }\n });\n\n // Bootstrap status — does an owner exist yet? Used by the Account SPA's\n // root route to decide between rendering /login (normal flow) and\n // /setup (first-run owner creation). Public, unauthenticated; only\n // returns a boolean so it can be polled before the user has any\n // credentials.\n rawApp.get(`${basePath}/bootstrap-status`, async (c: any) => {\n try {\n const dataEngine = this.authManager!.getDataEngine();\n if (!dataEngine) {\n // No data engine wired (e.g. MSW/mock mode) — assume bootstrapped\n // so the SPA falls through to its normal login flow.\n return c.json({ hasOwner: true });\n }\n const count = await dataEngine.count('sys_user', {});\n return c.json({ hasOwner: (count ?? 0) > 0 });\n } catch (error) {\n ctx.logger.warn('[AuthPlugin] bootstrap-status check failed; assuming bootstrapped', error as Error);\n return c.json({ hasOwner: true });\n }\n });\n\n // Device Authorization Grant (RFC 8628) endpoints — `/device/code`,\n // `/device/token`, `/device/approve`, `/device/deny`, `/device` — are\n // provided by better-auth's `device-authorization` plugin and reach\n // the wildcard handler below. Enable via\n // `AuthPluginConfig.deviceAuthorization`.\n\n // Set an INITIAL local password for users who signed in via SSO and\n // have no `credential` account yet. This is the \"Set local password\"\n // affordance on a per-environment kernel — it lets a user that the\n // platform onboarded via the objectstack-cloud OAuth provider sign in\n // with email/password against this environment going forward without\n // needing the SSO round-trip. Requires a valid session (so we know\n // which user is asking) and refuses if a credential already exists\n // (the user should use better-auth's /change-password endpoint in\n // that case so the current password is verified).\n rawApp.post(`${basePath}/set-initial-password`, async (c: any) => {\n try {\n let body: any = {};\n try { body = await c.req.json(); } catch { body = {}; }\n const newPassword: unknown = body?.newPassword;\n if (typeof newPassword !== 'string' || newPassword.length === 0) {\n return c.json({ success: false, error: { code: 'invalid_request', message: 'newPassword is required' } }, 400);\n }\n\n const authApi = await this.authManager!.getApi();\n const session = await authApi.getSession({ headers: c.req.raw.headers });\n if (!session?.user?.id) {\n return c.json({ success: false, error: { code: 'unauthorized', message: 'Sign in first' } }, 401);\n }\n const userId = session.user.id;\n\n const authCtx: any = await this.authManager!.getAuthContext();\n if (!authCtx?.internalAdapter || !authCtx?.password) {\n return c.json({ success: false, error: { code: 'unavailable', message: 'Auth context unavailable' } }, 503);\n }\n\n // Length checks mirror better-auth's emailAndPassword.{min,max}PasswordLength\n // so the validation surface is consistent across set / change / reset.\n const minLen = authCtx.password?.config?.minPasswordLength ?? 8;\n const maxLen = authCtx.password?.config?.maxPasswordLength ?? 128;\n if (newPassword.length < minLen) {\n return c.json({ success: false, error: { code: 'password_too_short', message: `Password must be at least ${minLen} characters` } }, 400);\n }\n if (newPassword.length > maxLen) {\n return c.json({ success: false, error: { code: 'password_too_long', message: `Password must be at most ${maxLen} characters` } }, 400);\n }\n\n const accounts = await authCtx.internalAdapter.findAccounts(userId);\n const existingCredential = accounts?.find?.((a: any) => a.providerId === 'credential' && a.password);\n if (existingCredential) {\n // Use /change-password (requires currentPassword) instead.\n return c.json({\n success: false,\n error: {\n code: 'credential_account_exists',\n message: 'A local password is already set for this account. Use change-password instead.',\n },\n }, 409);\n }\n\n const passwordHash = await authCtx.password.hash(newPassword);\n await authCtx.internalAdapter.createAccount({\n userId,\n providerId: 'credential',\n accountId: userId,\n password: passwordHash,\n });\n\n return c.json({ success: true });\n } catch (error) {\n const err = error instanceof Error ? error : new Error(String(error));\n ctx.logger.error('[AuthPlugin] set-initial-password failed', err);\n return c.json({ success: false, error: { code: 'internal', message: err.message } }, 500);\n }\n });\n\n // ────────────────────────────────────────────────────────────────────\n // OAuth admin: toggle the `disabled` flag on a registered OAuth client.\n //\n // Why this lives here (and not as a plain data-layer UPDATE on\n // sys_oauth_application): better-auth 1.6.11's stock admin update\n // endpoint (`/admin/oauth2/update-client`) does NOT accept `disabled`\n // in its Zod body schema, so the field gets silently stripped before\n // it reaches `updateClientEndpoint`. The column exists, the runtime\n // honours it everywhere (introspect, token, authorize, public-client\n // lookup), but no client-facing API can flip it.\n //\n // We close the gap by writing through better-auth's own adapter under\n // the `/api/v1/auth/*` namespace so all OAuth-application mutations\n // remain auth-routed (no generic data-layer bypass for the `oauth_client`\n // model). When upstream adds `disabled` to `adminUpdateOAuthClient`'s\n // schema, this route can be deleted and the sys_oauth_application\n // action retargeted to the stock endpoint.\n //\n // Upstream tracking: https://github.com/better-auth/better-auth\n rawApp.post(`${basePath}/admin/oauth2/toggle-disabled`, async (c: any) => {\n try {\n let body: any = {};\n try { body = await c.req.json(); } catch { body = {}; }\n const clientId: unknown = body?.client_id;\n const disabled: unknown = body?.disabled;\n if (typeof clientId !== 'string' || clientId.length === 0) {\n return c.json({ success: false, error: { code: 'invalid_request', message: 'client_id is required' } }, 400);\n }\n if (typeof disabled !== 'boolean') {\n return c.json({ success: false, error: { code: 'invalid_request', message: 'disabled must be a boolean' } }, 400);\n }\n\n const authApi = await this.authManager!.getApi();\n const session = await authApi.getSession({ headers: c.req.raw.headers });\n if (!session?.user?.id) {\n return c.json({ success: false, error: { code: 'unauthorized', message: 'Sign in first' } }, 401);\n }\n // The customSession plugin synthesizes `user.role = 'admin'` for\n // platform admins (admin_full_access permission set) and active-org\n // owners/admins; anyone else is denied.\n if ((session.user as any).role !== 'admin') {\n return c.json({ success: false, error: { code: 'forbidden', message: 'Admin role required' } }, 403);\n }\n\n // Write through the same ObjectQL data engine that better-auth's\n // adapter uses. We target the snake_case table name (`sys_oauth_application`,\n // mapped from better-auth's internal `oauthClient` model via\n // `auth-schema-config.ts`) because `$context.adapter`'s model-lookup\n // helper does not see plugin-provided model names from outside\n // better-auth's own endpoint invocation context. This is the same\n // physical row the better-auth runtime reads at introspect / token\n // / authorize time, so the toggle is fully honoured.\n const dataEngine: any = this.authManager!.getDataEngine();\n if (!dataEngine) {\n return c.json({ success: false, error: { code: 'unavailable', message: 'Data engine unavailable' } }, 503);\n }\n\n const existing = await dataEngine.findOne('sys_oauth_application', {\n where: { client_id: clientId },\n });\n if (!existing) {\n return c.json({ success: false, error: { code: 'not_found', message: 'OAuth client not found' } }, 404);\n }\n\n const updated = await dataEngine.update('sys_oauth_application', {\n id: existing.id,\n disabled,\n updated_at: new Date(Math.floor(Date.now() / 1000) * 1000),\n });\n if (!updated) {\n return c.json({ success: false, error: { code: 'internal', message: 'Unable to update OAuth client' } }, 500);\n }\n\n return c.json({\n success: true,\n data: {\n client_id: clientId,\n disabled,\n },\n });\n } catch (error) {\n const err = error instanceof Error ? error : new Error(String(error));\n ctx.logger.error('[AuthPlugin] toggle-disabled failed', err);\n return c.json({ success: false, error: { code: 'internal', message: err.message } }, 500);\n }\n });\n\n // Register wildcard route to forward all auth requests to better-auth.\n // better-auth is configured with basePath matching our route prefix, so we\n // forward the original request directly — no path rewriting needed.\n rawApp.all(`${basePath}/*`, async (c: any) => {\n try {\n // Forward the original request to better-auth handler\n const response = await this.authManager!.handleRequest(c.req.raw);\n\n // better-auth catches internal errors and returns error Responses\n // without throwing, so the catch block below would never trigger.\n // We proactively log server errors here for observability.\n if (response.status >= 500) {\n try {\n const body = await response.clone().text();\n ctx.logger.error('[AuthPlugin] better-auth returned server error', new Error(`HTTP ${response.status}: ${body}`));\n } catch {\n ctx.logger.error('[AuthPlugin] better-auth returned server error', new Error(`HTTP ${response.status}: (unable to read body)`));\n }\n }\n\n // Public-cache JWKS: it's static JSON that only changes when the\n // signing key rotates (default ~30 days). better-auth doesn't set\n // any Cache-Control header, so every relying party currently\n // re-fetches it on every JWT verification (≈700 ms warm against a\n // Container DO + Neon). Add a conservative public cache so CF's\n // edge can short-circuit repeated fetches. The 5-min freshness +\n // 24 h SWR window is well inside better-auth's default rotation\n // and matches what most IdPs publish (Auth0, Cognito, Google).\n try {\n const url = c.req.url as string;\n if (response.ok && /\\/jwks(\\?|$)/.test(url)) {\n const existing = response.headers.get('cache-control');\n if (!existing) {\n response.headers.set(\n 'cache-control',\n 'public, max-age=300, stale-while-revalidate=86400',\n );\n }\n }\n } catch { /* best-effort header annotation */ }\n\n return response;\n } catch (error) {\n const err = error instanceof Error ? error : new Error(String(error));\n ctx.logger.error('Auth request error:', err);\n \n // Return error response\n return new Response(\n JSON.stringify({\n success: false,\n error: err.message,\n }),\n {\n status: 500,\n headers: { 'Content-Type': 'application/json' },\n }\n );\n }\n });\n\n // OIDC / OAuth 2.0 Authorization Server Metadata (RFC 8414) and\n // OpenID Connect Discovery 1.0 require the well-known documents to be\n // served from the **root** of the issuer URL — not under our auth\n // basePath. `@better-auth/oauth-provider` ships dedicated helpers for\n // this case (`oauthProviderAuthServerMetadata` /\n // `oauthProviderOpenIdConfigMetadata`) which we mount here so external\n // OIDC clients can discover the IdP at the canonical paths.\n if (this.options.plugins?.oidcProvider) {\n void this.registerOidcDiscoveryRoutes(rawApp, ctx).catch((error) => {\n ctx.logger.error('Failed to register OIDC discovery routes', error as Error);\n });\n }\n\n ctx.logger.info(`Auth routes registered: All requests under ${basePath}/* forwarded to better-auth`);\n }\n\n /**\n * Mount the OIDC / OAuth 2.0 well-known discovery documents at the root\n * URL. Required by RFC 8414 §3 and OpenID Connect Discovery 1.0 §4 — the\n * documents must live at `/.well-known/{oauth-authorization-server,openid-configuration}`\n * relative to the issuer, not under the auth basePath.\n */\n private async registerOidcDiscoveryRoutes(rawApp: any, ctx: PluginContext): Promise<void> {\n const auth = await this.authManager!.getAuthInstance();\n const { oauthProviderAuthServerMetadata, oauthProviderOpenIdConfigMetadata } = await import(\n '@better-auth/oauth-provider'\n );\n\n const authServerHandler = oauthProviderAuthServerMetadata(auth as any);\n const openidConfigHandler = oauthProviderOpenIdConfigMetadata(auth as any);\n\n // Cache-Control for OIDC discovery docs. These describe stable issuer\n // configuration (endpoints, supported scopes, signing algs); they\n // change only on app redeploy. CF edge can short-circuit repeated\n // fetches and dramatically cut SSO first-call latency.\n const DISCOVERY_CACHE = 'public, max-age=300, stale-while-revalidate=86400';\n const withDiscoveryCache = async (handler: (req: Request) => Promise<Response> | Response, req: Request): Promise<Response> => {\n const resp = await handler(req);\n try {\n if (resp.ok && !resp.headers.get('cache-control')) {\n resp.headers.set('cache-control', DISCOVERY_CACHE);\n }\n } catch { /* best-effort */ }\n return resp;\n };\n\n rawApp.get('/.well-known/oauth-authorization-server', (c: any) => withDiscoveryCache(authServerHandler, c.req.raw));\n rawApp.get('/.well-known/openid-configuration', (c: any) => withDiscoveryCache(openidConfigHandler, c.req.raw));\n\n ctx.logger.info(\n 'OIDC discovery endpoints mounted at /.well-known/{oauth-authorization-server,openid-configuration}',\n );\n }\n}\n\n\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport type { IDataEngine } from '@objectstack/core';\nimport { createAdapterFactory } from 'better-auth/adapters';\nimport type { CleanedWhere } from 'better-auth/adapters';\nimport { SystemObjectName } from '@objectstack/spec/system';\n\n/**\n * Mapping from better-auth model names to ObjectStack protocol object names.\n *\n * better-auth uses hardcoded model names ('user', 'session', 'account', 'verification')\n * while ObjectStack's protocol layer uses `sys_` prefixed names. This map bridges the two.\n */\nexport const AUTH_MODEL_TO_PROTOCOL: Record<string, string> = {\n user: SystemObjectName.USER,\n session: SystemObjectName.SESSION,\n account: SystemObjectName.ACCOUNT,\n verification: SystemObjectName.VERIFICATION,\n};\n\n/**\n * Resolve a better-auth model name to the ObjectStack protocol object name.\n * Falls back to the original model name for custom / non-core models.\n */\nexport function resolveProtocolName(model: string): string {\n return AUTH_MODEL_TO_PROTOCOL[model] ?? model;\n}\n\n// ---------------------------------------------------------------------------\n// Internal helpers\n// ---------------------------------------------------------------------------\n\n/**\n * better-auth datetime columns (snake_case) per model.\n *\n * When the underlying driver stored these as JavaScript `Date` objects\n * (legacy behaviour), the libsql HTTP transport coerces the value to a REAL\n * column and round-trips it as a string like `\"1779497911249.0\"`. That\n * string is not a valid Date string (it has a trailing `.0`), so\n * `new Date(...)` produces `Invalid Date` and better-auth's client treats\n * the session as expired — causing a login/redirect loop.\n *\n * We normalise these legacy values back to ISO strings on **read** so the\n * factory's `supportsDates: false` parser can turn them into real Date\n * objects. New writes always go through better-auth's own\n * `Date → ISO string` conversion (because we declare `supportsDates: false`\n * below), so no further `.0`-suffixed values will ever be created.\n */\nconst LEGACY_DATETIME_FIELDS_BY_MODEL: Record<string, string[]> = {\n user: ['created_at', 'updated_at'],\n session: ['expires_at', 'created_at', 'updated_at'],\n account: [\n 'access_token_expires_at',\n 'refresh_token_expires_at',\n 'created_at',\n 'updated_at',\n ],\n verification: ['expires_at', 'created_at', 'updated_at'],\n};\n\nconst NUMERIC_STRING_RE = /^-?\\d+(\\.\\d+)?$/;\n\n/**\n * If `value` looks like a stringified epoch-ms (optionally with `.0`),\n * convert it to an ISO 8601 string. Otherwise return it unchanged.\n */\nfunction normaliseLegacyDate(value: unknown): unknown {\n if (typeof value !== 'string') return value;\n if (!NUMERIC_STRING_RE.test(value)) return value;\n const n = parseFloat(value);\n if (!Number.isFinite(n)) return value;\n // Heuristic: epoch milliseconds are at least 10 digits (year 2001+).\n if (Math.abs(n) < 1e10) return value;\n const d = new Date(n);\n if (Number.isNaN(d.getTime())) return value;\n return d.toISOString();\n}\n\n/**\n * Walk a record and rewrite any legacy `.0`-suffixed datetime values\n * into ISO strings. Mutates and returns the record.\n */\nfunction normaliseLegacyDates<T extends Record<string, any> | null | undefined>(\n model: string,\n record: T,\n): T {\n if (!record) return record;\n const cols = LEGACY_DATETIME_FIELDS_BY_MODEL[model];\n if (!cols) return record;\n for (const col of cols) {\n if (col in record) {\n (record as Record<string, unknown>)[col] = normaliseLegacyDate(\n (record as Record<string, unknown>)[col],\n );\n }\n }\n return record;\n}\n\n/**\n * Convert better-auth where clause to ObjectQL query format.\n *\n * Field names in the incoming {@link CleanedWhere} are expected to already be\n * in snake_case (transformed by `createAdapterFactory`).\n */\nfunction convertWhere(where: CleanedWhere[]): Record<string, any> {\n const filter: Record<string, any> = {};\n\n for (const condition of where) {\n const fieldName = condition.field;\n\n if (condition.operator === 'eq') {\n filter[fieldName] = condition.value;\n } else if (condition.operator === 'ne') {\n filter[fieldName] = { $ne: condition.value };\n } else if (condition.operator === 'in') {\n filter[fieldName] = { $in: condition.value };\n } else if (condition.operator === 'gt') {\n filter[fieldName] = { $gt: condition.value };\n } else if (condition.operator === 'gte') {\n filter[fieldName] = { $gte: condition.value };\n } else if (condition.operator === 'lt') {\n filter[fieldName] = { $lt: condition.value };\n } else if (condition.operator === 'lte') {\n filter[fieldName] = { $lte: condition.value };\n } else if (condition.operator === 'contains') {\n filter[fieldName] = { $regex: condition.value };\n }\n }\n\n return filter;\n}\n\n// ---------------------------------------------------------------------------\n// Adapter factory\n// ---------------------------------------------------------------------------\n\n/**\n * Create an ObjectQL adapter **factory** for better-auth.\n *\n * Uses better-auth's official `createAdapterFactory` so that model-name and\n * field-name transformations (declared via `modelName` / `fields` in the\n * betterAuth config) are applied **automatically** before any data reaches\n * ObjectQL. This eliminates the need for manual camelCase ↔ snake_case\n * conversion inside the adapter.\n *\n * The returned value is an `AdapterFactory` – a function of type\n * `(options: BetterAuthOptions) => DBAdapter` – which is the shape expected\n * by `betterAuth({ database: … })`.\n *\n * @param dataEngine - ObjectQL data engine instance\n * @returns better-auth AdapterFactory\n */\nexport function createObjectQLAdapterFactory(dataEngine: IDataEngine) {\n return createAdapterFactory({\n config: {\n adapterId: 'objectql',\n // We let better-auth handle Date↔string and boolean↔0/1 conversion so\n // that values land in the underlying SQL driver as primitive strings\n // and integers. Some drivers (e.g. libsql over the HTTP transport)\n // otherwise mangle `Date` objects into `\"<epoch>.0\"` strings that\n // break the client-side session parser.\n supportsBooleans: false,\n supportsDates: false,\n supportsJSON: true,\n },\n adapter: () => ({\n create: async <T extends Record<string, any>>(\n { model, data, select: _select }: { model: string; data: T; select?: string[] },\n ): Promise<T> => {\n const result = await dataEngine.insert(model, data);\n return normaliseLegacyDates(model, result) as T;\n },\n\n findOne: async <T>(\n { model, where, select, join: _join }: { model: string; where: CleanedWhere[]; select?: string[]; join?: any },\n ): Promise<T | null> => {\n const filter = convertWhere(where);\n\n const result = await dataEngine.findOne(model, { where: filter, fields: select });\n\n return result ? (normaliseLegacyDates(model, result) as T) : null;\n },\n\n findMany: async <T>(\n { model, where, limit, offset, sortBy, join: _join }: {\n model: string; where?: CleanedWhere[]; limit: number;\n offset?: number; sortBy?: { field: string; direction: 'asc' | 'desc' }; join?: any;\n },\n ): Promise<T[]> => {\n const filter = where ? convertWhere(where) : {};\n\n const orderBy = sortBy\n ? [{ field: sortBy.field, order: sortBy.direction as 'asc' | 'desc' }]\n : undefined;\n\n const results = await dataEngine.find(model, {\n where: filter,\n limit: limit || 100,\n offset,\n orderBy,\n });\n\n return results.map((r) => normaliseLegacyDates(model, r as Record<string, any>)) as T[];\n },\n\n count: async (\n { model, where }: { model: string; where?: CleanedWhere[] },\n ): Promise<number> => {\n const filter = where ? convertWhere(where) : {};\n return await dataEngine.count(model, { where: filter });\n },\n\n update: async <T>(\n { model, where, update }: { model: string; where: CleanedWhere[]; update: T },\n ): Promise<T | null> => {\n const filter = convertWhere(where);\n\n // ObjectQL requires an ID for updates – find the record first\n const record = await dataEngine.findOne(model, { where: filter });\n if (!record) return null;\n\n const result = await dataEngine.update(model, { ...(update as any), id: record.id });\n return result ? (normaliseLegacyDates(model, result) as T) : null;\n },\n\n updateMany: async (\n { model, where, update }: { model: string; where: CleanedWhere[]; update: Record<string, any> },\n ): Promise<number> => {\n const filter = convertWhere(where);\n\n // Sequential updates: ObjectQL requires an ID per update\n const records = await dataEngine.find(model, { where: filter });\n for (const record of records) {\n await dataEngine.update(model, { ...update, id: record.id });\n }\n return records.length;\n },\n\n delete: async (\n { model, where }: { model: string; where: CleanedWhere[] },\n ): Promise<void> => {\n const filter = convertWhere(where);\n\n const record = await dataEngine.findOne(model, { where: filter });\n if (!record) return;\n\n await dataEngine.delete(model, { where: { id: record.id } });\n },\n\n deleteMany: async (\n { model, where }: { model: string; where: CleanedWhere[] },\n ): Promise<number> => {\n const filter = convertWhere(where);\n\n const records = await dataEngine.find(model, { where: filter });\n for (const record of records) {\n await dataEngine.delete(model, { where: { id: record.id } });\n }\n return records.length;\n },\n }),\n });\n}\n\n// ---------------------------------------------------------------------------\n// Legacy adapter (kept for backward compatibility)\n// ---------------------------------------------------------------------------\n\n/**\n * Create a raw ObjectQL adapter for better-auth (without factory wrapping).\n *\n * > **Prefer {@link createObjectQLAdapterFactory}** for production use.\n * > The factory version leverages `createAdapterFactory` and automatically\n * > handles model-name + field-name transformations declared in the\n * > better-auth config.\n *\n * This function is retained for direct / low-level usage where callers\n * manage field-name conversion themselves.\n *\n * @param dataEngine - ObjectQL data engine instance\n * @returns better-auth CustomAdapter (raw, without factory wrapping)\n */\nexport function createObjectQLAdapter(dataEngine: IDataEngine) {\n return {\n create: async <T extends Record<string, any>>({ model, data, select: _select }: { model: string; data: T; select?: string[] }): Promise<T> => {\n const objectName = resolveProtocolName(model);\n const result = await dataEngine.insert(objectName, data);\n return result as T;\n },\n\n findOne: async <T>({ model, where, select, join: _join }: { model: string; where: CleanedWhere[]; select?: string[]; join?: any }): Promise<T | null> => {\n const objectName = resolveProtocolName(model);\n const filter = convertWhere(where);\n const result = await dataEngine.findOne(objectName, { where: filter, fields: select });\n return result ? result as T : null;\n },\n\n findMany: async <T>({ model, where, limit, offset, sortBy, join: _join }: { model: string; where?: CleanedWhere[]; limit: number; offset?: number; sortBy?: { field: string; direction: 'asc' | 'desc' }; join?: any }): Promise<T[]> => {\n const objectName = resolveProtocolName(model);\n const filter = where ? convertWhere(where) : {};\n const orderBy = sortBy ? [{ field: sortBy.field, order: sortBy.direction as 'asc' | 'desc' }] : undefined;\n const results = await dataEngine.find(objectName, { where: filter, limit: limit || 100, offset, orderBy });\n return results as T[];\n },\n\n count: async ({ model, where }: { model: string; where?: CleanedWhere[] }): Promise<number> => {\n const objectName = resolveProtocolName(model);\n const filter = where ? convertWhere(where) : {};\n return await dataEngine.count(objectName, { where: filter });\n },\n\n update: async <T>({ model, where, update }: { model: string; where: CleanedWhere[]; update: Record<string, any> }): Promise<T | null> => {\n const objectName = resolveProtocolName(model);\n const filter = convertWhere(where);\n const record = await dataEngine.findOne(objectName, { where: filter });\n if (!record) return null;\n const result = await dataEngine.update(objectName, { ...update, id: record.id });\n return result ? result as T : null;\n },\n\n updateMany: async ({ model, where, update }: { model: string; where: CleanedWhere[]; update: Record<string, any> }): Promise<number> => {\n const objectName = resolveProtocolName(model);\n const filter = convertWhere(where);\n const records = await dataEngine.find(objectName, { where: filter });\n for (const record of records) {\n await dataEngine.update(objectName, { ...update, id: record.id });\n }\n return records.length;\n },\n\n delete: async ({ model, where }: { model: string; where: CleanedWhere[] }): Promise<void> => {\n const objectName = resolveProtocolName(model);\n const filter = convertWhere(where);\n const record = await dataEngine.findOne(objectName, { where: filter });\n if (!record) return;\n await dataEngine.delete(objectName, { where: { id: record.id } });\n },\n\n deleteMany: async ({ model, where }: { model: string; where: CleanedWhere[] }): Promise<number> => {\n const objectName = resolveProtocolName(model);\n const filter = convertWhere(where);\n const records = await dataEngine.find(objectName, { where: filter });\n for (const record of records) {\n await dataEngine.delete(objectName, { where: { id: record.id } });\n }\n return records.length;\n },\n };\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport { SystemObjectName } from '@objectstack/spec/system';\n\n/**\n * better-auth ↔ ObjectStack Schema Mapping\n *\n * better-auth uses camelCase field names internally (e.g. `emailVerified`, `userId`)\n * while ObjectStack's protocol layer uses snake_case (e.g. `email_verified`, `user_id`).\n *\n * These constants declare the `modelName` and `fields` mappings for each core auth\n * model, following better-auth's official schema customisation API\n * ({@link https://www.better-auth.com/docs/concepts/database}).\n *\n * The mappings serve two purposes:\n * 1. `modelName` — maps the default model name to the ObjectStack protocol name\n * (e.g. `user` → `sys_user`).\n * 2. `fields` — maps camelCase field names to their snake_case database column\n * equivalents. Only fields whose names differ need to be listed; fields that\n * are already identical (e.g. `email`, `name`, `token`) are omitted.\n *\n * These mappings are consumed by:\n * - The `betterAuth()` configuration in {@link AuthManager} so that\n * `getAuthTables()` builds the correct schema.\n * - The ObjectQL adapter factory (via `createAdapterFactory`) which uses the\n * schema to transform data and where-clauses automatically.\n */\n\n// ---------------------------------------------------------------------------\n// User model\n// ---------------------------------------------------------------------------\n\n/**\n * better-auth `user` model mapping.\n *\n * | camelCase (better-auth) | snake_case (ObjectStack) |\n * |:------------------------|:-------------------------|\n * | emailVerified | email_verified |\n * | createdAt | created_at |\n * | updatedAt | updated_at |\n */\nexport const AUTH_USER_CONFIG = {\n modelName: SystemObjectName.USER, // 'sys_user'\n fields: {\n emailVerified: 'email_verified',\n createdAt: 'created_at',\n updatedAt: 'updated_at',\n },\n} as const;\n\n// ---------------------------------------------------------------------------\n// Session model\n// ---------------------------------------------------------------------------\n\n/**\n * better-auth `session` model mapping.\n *\n * | camelCase (better-auth) | snake_case (ObjectStack) |\n * |:------------------------|:-------------------------|\n * | userId | user_id |\n * | expiresAt | expires_at |\n * | createdAt | created_at |\n * | updatedAt | updated_at |\n * | ipAddress | ip_address |\n * | userAgent | user_agent |\n */\nexport const AUTH_SESSION_CONFIG = {\n modelName: SystemObjectName.SESSION, // 'sys_session'\n fields: {\n userId: 'user_id',\n expiresAt: 'expires_at',\n createdAt: 'created_at',\n updatedAt: 'updated_at',\n ipAddress: 'ip_address',\n userAgent: 'user_agent',\n },\n} as const;\n\n// ---------------------------------------------------------------------------\n// Account model\n// ---------------------------------------------------------------------------\n\n/**\n * better-auth `account` model mapping.\n *\n * | camelCase (better-auth) | snake_case (ObjectStack) |\n * |:--------------------------|:-------------------------------|\n * | userId | user_id |\n * | providerId | provider_id |\n * | accountId | account_id |\n * | accessToken | access_token |\n * | refreshToken | refresh_token |\n * | idToken | id_token |\n * | accessTokenExpiresAt | access_token_expires_at |\n * | refreshTokenExpiresAt | refresh_token_expires_at |\n * | createdAt | created_at |\n * | updatedAt | updated_at |\n */\nexport const AUTH_ACCOUNT_CONFIG = {\n modelName: SystemObjectName.ACCOUNT, // 'sys_account'\n fields: {\n userId: 'user_id',\n providerId: 'provider_id',\n accountId: 'account_id',\n accessToken: 'access_token',\n refreshToken: 'refresh_token',\n idToken: 'id_token',\n accessTokenExpiresAt: 'access_token_expires_at',\n refreshTokenExpiresAt: 'refresh_token_expires_at',\n createdAt: 'created_at',\n updatedAt: 'updated_at',\n },\n} as const;\n\n// ---------------------------------------------------------------------------\n// Verification model\n// ---------------------------------------------------------------------------\n\n/**\n * better-auth `verification` model mapping.\n *\n * | camelCase (better-auth) | snake_case (ObjectStack) |\n * |:------------------------|:-------------------------|\n * | expiresAt | expires_at |\n * | createdAt | created_at |\n * | updatedAt | updated_at |\n */\nexport const AUTH_VERIFICATION_CONFIG = {\n modelName: SystemObjectName.VERIFICATION, // 'sys_verification'\n fields: {\n expiresAt: 'expires_at',\n createdAt: 'created_at',\n updatedAt: 'updated_at',\n },\n} as const;\n\n// ===========================================================================\n// Plugin Table Mappings\n// ===========================================================================\n//\n// better-auth plugins (organization, two-factor, etc.) introduce additional\n// tables with their own camelCase field names. The mappings below are passed\n// to the plugin's `schema` option so that `createAdapterFactory` transforms\n// them to snake_case automatically, just like the core models above.\n// ===========================================================================\n\n// ---------------------------------------------------------------------------\n// Organization plugin – organization table\n// ---------------------------------------------------------------------------\n\n/**\n * better-auth Organization plugin `organization` model mapping.\n *\n * | camelCase (better-auth) | snake_case (ObjectStack) |\n * |:------------------------|:-------------------------|\n * | createdAt | created_at |\n * | updatedAt | updated_at |\n */\nexport const AUTH_ORGANIZATION_SCHEMA = {\n modelName: SystemObjectName.ORGANIZATION, // 'sys_organization'\n fields: {\n createdAt: 'created_at',\n updatedAt: 'updated_at',\n },\n} as const;\n\n// ---------------------------------------------------------------------------\n// Organization plugin – member table\n// ---------------------------------------------------------------------------\n\n/**\n * better-auth Organization plugin `member` model mapping.\n *\n * | camelCase (better-auth) | snake_case (ObjectStack) |\n * |:------------------------|:-------------------------|\n * | organizationId | organization_id |\n * | userId | user_id |\n * | createdAt | created_at |\n */\nexport const AUTH_MEMBER_SCHEMA = {\n modelName: SystemObjectName.MEMBER, // 'sys_member'\n fields: {\n organizationId: 'organization_id',\n userId: 'user_id',\n createdAt: 'created_at',\n },\n} as const;\n\n// ---------------------------------------------------------------------------\n// Organization plugin – invitation table\n// ---------------------------------------------------------------------------\n\n/**\n * better-auth Organization plugin `invitation` model mapping.\n *\n * | camelCase (better-auth) | snake_case (ObjectStack) |\n * |:------------------------|:-------------------------|\n * | organizationId | organization_id |\n * | inviterId | inviter_id |\n * | expiresAt | expires_at |\n * | createdAt | created_at |\n * | teamId | team_id |\n */\nexport const AUTH_INVITATION_SCHEMA = {\n modelName: SystemObjectName.INVITATION, // 'sys_invitation'\n fields: {\n organizationId: 'organization_id',\n inviterId: 'inviter_id',\n expiresAt: 'expires_at',\n createdAt: 'created_at',\n teamId: 'team_id',\n },\n} as const;\n\n// ---------------------------------------------------------------------------\n// Organization plugin – session additional fields\n// ---------------------------------------------------------------------------\n\n/**\n * Organization plugin adds `activeOrganizationId` (and optionally\n * `activeTeamId`) to the session model. These field mappings are\n * injected via the organization plugin's `schema.session.fields`.\n */\nexport const AUTH_ORG_SESSION_FIELDS = {\n activeOrganizationId: 'active_organization_id',\n activeTeamId: 'active_team_id',\n} as const;\n\n// ---------------------------------------------------------------------------\n// Organization plugin – team table (optional, when teams enabled)\n// ---------------------------------------------------------------------------\n\n/**\n * better-auth Organization plugin `team` model mapping.\n *\n * | camelCase (better-auth) | snake_case (ObjectStack) |\n * |:------------------------|:-------------------------|\n * | organizationId | organization_id |\n * | createdAt | created_at |\n * | updatedAt | updated_at |\n */\nexport const AUTH_TEAM_SCHEMA = {\n modelName: SystemObjectName.TEAM, // 'sys_team'\n fields: {\n organizationId: 'organization_id',\n createdAt: 'created_at',\n updatedAt: 'updated_at',\n },\n} as const;\n\n// ---------------------------------------------------------------------------\n// Organization plugin – teamMember table (optional, when teams enabled)\n// ---------------------------------------------------------------------------\n\n/**\n * better-auth Organization plugin `teamMember` model mapping.\n *\n * | camelCase (better-auth) | snake_case (ObjectStack) |\n * |:------------------------|:-------------------------|\n * | teamId | team_id |\n * | userId | user_id |\n * | createdAt | created_at |\n */\nexport const AUTH_TEAM_MEMBER_SCHEMA = {\n modelName: SystemObjectName.TEAM_MEMBER, // 'sys_team_member'\n fields: {\n teamId: 'team_id',\n userId: 'user_id',\n createdAt: 'created_at',\n },\n} as const;\n\n// ---------------------------------------------------------------------------\n// Two-Factor plugin – twoFactor table\n// ---------------------------------------------------------------------------\n\n/**\n * better-auth Two-Factor plugin `twoFactor` model mapping.\n *\n * | camelCase (better-auth) | snake_case (ObjectStack) |\n * |:------------------------|:-------------------------|\n * | backupCodes | backup_codes |\n * | userId | user_id |\n */\nexport const AUTH_TWO_FACTOR_SCHEMA = {\n modelName: SystemObjectName.TWO_FACTOR, // 'sys_two_factor'\n fields: {\n backupCodes: 'backup_codes',\n userId: 'user_id',\n },\n} as const;\n\n/**\n * Two-Factor plugin adds a `twoFactorEnabled` field to the user model.\n */\nexport const AUTH_TWO_FACTOR_USER_FIELDS = {\n twoFactorEnabled: 'two_factor_enabled',\n} as const;\n\n// ---------------------------------------------------------------------------\n// Admin plugin – user/session field additions\n// ---------------------------------------------------------------------------\n\n/**\n * Admin plugin adds platform-level admin fields to the `user` model.\n *\n * | camelCase (better-auth) | snake_case (ObjectStack) |\n * |:------------------------|:-------------------------|\n * | banReason | ban_reason |\n * | banExpires | ban_expires |\n *\n * `role` and `banned` already have matching snake_case names and are\n * therefore omitted from this mapping (better-auth's database hooks\n * read them by the auto-derived column names).\n */\nexport const AUTH_ADMIN_USER_FIELDS = {\n banReason: 'ban_reason',\n banExpires: 'ban_expires',\n} as const;\n\n/**\n * Admin plugin adds an `impersonatedBy` field to the session model\n * recording the operator user id when an admin impersonates someone.\n */\nexport const AUTH_ADMIN_SESSION_FIELDS = {\n impersonatedBy: 'impersonated_by',\n} as const;\n\n// ---------------------------------------------------------------------------\n// OAuth Provider plugin – oauthClient table\n// ---------------------------------------------------------------------------\n\n/**\n * `@better-auth/oauth-provider` plugin `oauthClient` model mapping.\n *\n * The model name (`oauthClient`) is mapped to the existing\n * `sys_oauth_application` table to preserve data continuity from the\n * deprecated `oidc-provider` plugin.\n *\n * | camelCase (better-auth) | snake_case (ObjectStack) |\n * |:---------------------------|:--------------------------------|\n * | clientId | client_id |\n * | clientSecret | client_secret |\n * | skipConsent | skip_consent |\n * | enableEndSession | enable_end_session |\n * | subjectType | subject_type |\n * | userId | user_id |\n * | createdAt | created_at |\n * | updatedAt | updated_at |\n * | redirectUris | redirect_uris |\n * | postLogoutRedirectUris | post_logout_redirect_uris |\n * | tokenEndpointAuthMethod | token_endpoint_auth_method |\n * | grantTypes | grant_types |\n * | responseTypes | response_types |\n * | requirePKCE | require_pkce |\n * | softwareId | software_id |\n * | softwareVersion | software_version |\n * | softwareStatement | software_statement |\n * | referenceId | reference_id |\n */\nexport const AUTH_OAUTH_CLIENT_SCHEMA = {\n modelName: SystemObjectName.OAUTH_APPLICATION, // 'sys_oauth_application'\n fields: {\n clientId: 'client_id',\n clientSecret: 'client_secret',\n skipConsent: 'skip_consent',\n enableEndSession: 'enable_end_session',\n subjectType: 'subject_type',\n userId: 'user_id',\n createdAt: 'created_at',\n updatedAt: 'updated_at',\n redirectUris: 'redirect_uris',\n postLogoutRedirectUris: 'post_logout_redirect_uris',\n tokenEndpointAuthMethod: 'token_endpoint_auth_method',\n grantTypes: 'grant_types',\n responseTypes: 'response_types',\n requirePKCE: 'require_pkce',\n softwareId: 'software_id',\n softwareVersion: 'software_version',\n softwareStatement: 'software_statement',\n referenceId: 'reference_id',\n },\n} as const;\n\n/**\n * @deprecated Use {@link AUTH_OAUTH_CLIENT_SCHEMA}. Retained as an alias for\n * historical imports; the new package renamed `oauthApplication` → `oauthClient`.\n */\nexport const AUTH_OAUTH_APPLICATION_SCHEMA = AUTH_OAUTH_CLIENT_SCHEMA;\n\n// ---------------------------------------------------------------------------\n// OAuth Provider plugin – oauthAccessToken table\n// ---------------------------------------------------------------------------\n\n/**\n * `@better-auth/oauth-provider` plugin `oauthAccessToken` model mapping.\n *\n * In the new package, access tokens and refresh tokens are stored in\n * **separate** models. `oauthAccessToken` no longer carries a refresh token;\n * see {@link AUTH_OAUTH_REFRESH_TOKEN_SCHEMA} for the companion model.\n *\n * | camelCase (better-auth) | snake_case (ObjectStack) |\n * |:------------------------|:-------------------------|\n * | clientId | client_id |\n * | sessionId | session_id |\n * | userId | user_id |\n * | referenceId | reference_id |\n * | refreshId | refresh_id |\n * | expiresAt | expires_at |\n * | createdAt | created_at |\n */\nexport const AUTH_OAUTH_ACCESS_TOKEN_SCHEMA = {\n modelName: SystemObjectName.OAUTH_ACCESS_TOKEN, // 'sys_oauth_access_token'\n fields: {\n clientId: 'client_id',\n sessionId: 'session_id',\n userId: 'user_id',\n referenceId: 'reference_id',\n refreshId: 'refresh_id',\n expiresAt: 'expires_at',\n createdAt: 'created_at',\n },\n} as const;\n\n// ---------------------------------------------------------------------------\n// OAuth Provider plugin – oauthRefreshToken table\n// ---------------------------------------------------------------------------\n\n/**\n * `@better-auth/oauth-provider` plugin `oauthRefreshToken` model mapping.\n *\n * Refresh tokens are linked to a session (via `session_id`) and to the\n * issuing client. Each access token rotation produces a new refresh-token\n * row.\n *\n * | camelCase (better-auth) | snake_case (ObjectStack) |\n * |:------------------------|:-------------------------|\n * | clientId | client_id |\n * | sessionId | session_id |\n * | userId | user_id |\n * | referenceId | reference_id |\n * | expiresAt | expires_at |\n * | createdAt | created_at |\n * | authTime | auth_time |\n */\nexport const AUTH_OAUTH_REFRESH_TOKEN_SCHEMA = {\n modelName: SystemObjectName.OAUTH_REFRESH_TOKEN, // 'sys_oauth_refresh_token'\n fields: {\n clientId: 'client_id',\n sessionId: 'session_id',\n userId: 'user_id',\n referenceId: 'reference_id',\n expiresAt: 'expires_at',\n createdAt: 'created_at',\n authTime: 'auth_time',\n },\n} as const;\n\n// ---------------------------------------------------------------------------\n// OAuth Provider plugin – oauthConsent table\n// ---------------------------------------------------------------------------\n\n/**\n * `@better-auth/oauth-provider` plugin `oauthConsent` model mapping.\n *\n * The new package dropped the boolean `consentGiven` flag — the presence of\n * a row implies consent was given for the listed scopes. A new\n * `referenceId` column was added for client-supplied correlation.\n *\n * | camelCase (better-auth) | snake_case (ObjectStack) |\n * |:------------------------|:-------------------------|\n * | clientId | client_id |\n * | userId | user_id |\n * | referenceId | reference_id |\n * | createdAt | created_at |\n * | updatedAt | updated_at |\n */\nexport const AUTH_OAUTH_CONSENT_SCHEMA = {\n modelName: SystemObjectName.OAUTH_CONSENT, // 'sys_oauth_consent'\n fields: {\n clientId: 'client_id',\n userId: 'user_id',\n referenceId: 'reference_id',\n createdAt: 'created_at',\n updatedAt: 'updated_at',\n },\n} as const;\n\n// ---------------------------------------------------------------------------\n// Device Authorization plugin – deviceCode table\n// ---------------------------------------------------------------------------\n\n/**\n * better-auth `device-authorization` plugin `deviceCode` model mapping.\n *\n * Implements RFC 8628 (OAuth 2.0 Device Authorization Grant). Stores\n * pending device-flow requests issued via `POST /device/code`, polled at\n * `POST /device/token`, and approved/denied via `POST /device/{approve,deny}`.\n *\n * | camelCase (better-auth) | snake_case (ObjectStack) |\n * |:------------------------|:-------------------------|\n * | deviceCode | device_code |\n * | userCode | user_code |\n * | userId | user_id |\n * | expiresAt | expires_at |\n * | lastPolledAt | last_polled_at |\n * | pollingInterval | polling_interval |\n * | clientId | client_id |\n */\nexport const AUTH_DEVICE_CODE_SCHEMA = {\n modelName: SystemObjectName.DEVICE_CODE, // 'sys_device_code'\n fields: {\n deviceCode: 'device_code',\n userCode: 'user_code',\n userId: 'user_id',\n expiresAt: 'expires_at',\n lastPolledAt: 'last_polled_at',\n pollingInterval: 'polling_interval',\n clientId: 'client_id',\n },\n} as const;\n\n/**\n * Builds the `schema` option for better-auth's `twoFactor()` plugin.\n *\n * @returns An object suitable for `twoFactor({ schema: … })`\n */\nexport function buildTwoFactorPluginSchema() {\n return {\n twoFactor: AUTH_TWO_FACTOR_SCHEMA,\n user: {\n fields: AUTH_TWO_FACTOR_USER_FIELDS,\n },\n };\n}\n\n/**\n * Builds the `schema` option for better-auth's `admin()` plugin.\n *\n * The admin plugin extends the user model with `role`/`banned`/`banReason`/\n * `banExpires` and the session model with `impersonatedBy`. Only the\n * snake_case-differing fields are mapped explicitly.\n */\nexport function buildAdminPluginSchema() {\n return {\n user: {\n fields: AUTH_ADMIN_USER_FIELDS,\n },\n session: {\n fields: AUTH_ADMIN_SESSION_FIELDS,\n },\n };\n}\n\n// ---------------------------------------------------------------------------\n// Helper: build organization plugin schema option\n// ---------------------------------------------------------------------------\n\n/**\n * Builds the `schema` option for better-auth's `organization()` plugin.\n *\n * The organization plugin accepts a `schema` sub-option that allows\n * customising model names and field names for each table it manages.\n * This helper assembles the correct snake_case mappings from the\n * individual `AUTH_*_SCHEMA` constants above.\n *\n * @returns An object suitable for `organization({ schema: … })`\n */\nexport function buildOrganizationPluginSchema() {\n return {\n organization: AUTH_ORGANIZATION_SCHEMA,\n member: AUTH_MEMBER_SCHEMA,\n invitation: AUTH_INVITATION_SCHEMA,\n team: AUTH_TEAM_SCHEMA,\n teamMember: AUTH_TEAM_MEMBER_SCHEMA,\n session: {\n fields: AUTH_ORG_SESSION_FIELDS,\n },\n };\n}\n\n// ---------------------------------------------------------------------------\n// JWT plugin – jwks table\n// ---------------------------------------------------------------------------\n\n/**\n * better-auth `jwt` plugin `jwks` model mapping.\n *\n * The JWT plugin maintains a small set of rotating asymmetric key pairs\n * used to sign and verify issued JWTs (id_tokens for OIDC, JWT access\n * tokens). It is required by the `@better-auth/oauth-provider` plugin.\n *\n * | camelCase (better-auth) | snake_case (ObjectStack) |\n * |:------------------------|:-------------------------|\n * | publicKey | public_key |\n * | privateKey | private_key |\n * | createdAt | created_at |\n * | expiresAt | expires_at |\n */\nexport const AUTH_JWKS_SCHEMA = {\n modelName: SystemObjectName.JWKS, // 'sys_jwks'\n fields: {\n publicKey: 'public_key',\n privateKey: 'private_key',\n createdAt: 'created_at',\n expiresAt: 'expires_at',\n },\n} as const;\n\n/**\n * Builds the `schema` option for better-auth's `jwt()` plugin.\n *\n * @returns An object suitable for `jwt({ schema: … })`\n */\nexport function buildJwtPluginSchema() {\n return {\n jwks: AUTH_JWKS_SCHEMA,\n };\n}\n\n// ---------------------------------------------------------------------------\n// Helper: build OAuth provider plugin schema option\n// ---------------------------------------------------------------------------\n\n/**\n * Builds the `schema` option for `@better-auth/oauth-provider`'s\n * `oauthProvider()` plugin.\n *\n * The plugin manages four tables: `oauthClient` (registered client apps —\n * mapped to ObjectStack's `sys_oauth_application` table for backwards\n * compatibility), `oauthAccessToken` (issued access tokens),\n * `oauthRefreshToken` (issued refresh tokens, linked to a session), and\n * `oauthConsent` (recorded user consents).\n *\n * @returns An object suitable for `oauthProvider({ schema: … })`\n */\nexport function buildOauthProviderPluginSchema() {\n return {\n oauthClient: AUTH_OAUTH_CLIENT_SCHEMA,\n oauthAccessToken: AUTH_OAUTH_ACCESS_TOKEN_SCHEMA,\n oauthRefreshToken: AUTH_OAUTH_REFRESH_TOKEN_SCHEMA,\n oauthConsent: AUTH_OAUTH_CONSENT_SCHEMA,\n };\n}\n\n/**\n * @deprecated Use {@link buildOauthProviderPluginSchema}. Retained as an\n * alias for callers that imported the previous name during the migration\n * from the deprecated `better-auth/plugins/oidc-provider` plugin.\n */\nexport const buildOidcProviderPluginSchema = buildOauthProviderPluginSchema;\n\n// ---------------------------------------------------------------------------\n// Helper: build device-authorization plugin schema option\n// ---------------------------------------------------------------------------\n\n/**\n * Builds the `schema` option for better-auth's `deviceAuthorization()` plugin.\n *\n * The plugin manages a single `deviceCode` table tracking pending RFC 8628\n * device-flow requests. This helper returns the snake_case mappings that\n * point the plugin at ObjectStack's `sys_device_code` object.\n *\n * @returns An object suitable for `deviceAuthorization({ schema: … })`\n */\nexport function buildDeviceAuthorizationPluginSchema() {\n return {\n deviceCode: AUTH_DEVICE_CODE_SCHEMA,\n };\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport type { Auth, BetterAuthOptions } from 'better-auth';\n// better-auth value imports (betterAuth + plugins) are deferred via dynamic\n// import() in getOrCreateAuth() / buildPluginList() so that disabled plugins\n// never get loaded into the process. See Stage 2F (RSS investigation).\nimport type {\n AuthConfig,\n EmailAndPasswordConfig,\n AuthPluginConfig,\n OidcProvidersConfig,\n} from '@objectstack/spec/system';\nimport type { IDataEngine } from '@objectstack/core';\nimport type { IEmailService } from '@objectstack/spec/contracts';\nimport { createObjectQLAdapterFactory } from './objectql-adapter.js';\nimport {\n AUTH_USER_CONFIG,\n AUTH_SESSION_CONFIG,\n AUTH_ACCOUNT_CONFIG,\n AUTH_VERIFICATION_CONFIG,\n buildOrganizationPluginSchema,\n buildTwoFactorPluginSchema,\n buildOauthProviderPluginSchema,\n buildDeviceAuthorizationPluginSchema,\n buildJwtPluginSchema,\n buildAdminPluginSchema,\n} from './auth-schema-config.js';\n\n/**\n * Detect WebContainer (StackBlitz) environment.\n *\n * WebContainer reports itself as Node.js but runs inside a browser. Several\n * Node APIs are polyfilled with significant behavioural differences — most\n * notably `node:async_hooks.AsyncLocalStorage`, whose `run()` does NOT\n * propagate the store across `await` boundaries the way Node's native\n * implementation does.\n */\nfunction isWebContainerRuntime(): boolean {\n if (typeof globalThis === 'undefined') return false;\n const proc = (globalThis as any).process;\n return (\n Boolean(proc?.versions?.webcontainer) ||\n Boolean(proc?.env?.SHELL?.includes?.('jsh')) ||\n Boolean(proc?.env?.STACKBLITZ)\n );\n}\n\n/**\n * Synchronous AsyncLocalStorage polyfill compatible with better-auth's\n * `requestStateAsyncStorage` slot.\n *\n * Behaviour:\n * - `run(store, fn)` sets the current store synchronously before invoking\n * `fn` and restores the previous store after `fn` (and any promise it\n * returns) settles.\n * - `getStore()` returns the current store.\n *\n * Why a polyfill is needed in WebContainer:\n * - WebContainer's `node:async_hooks` does not propagate ALS context through\n * `await`, so better-auth's `runWithRequestState(map, () => handler(req))`\n * wrap loses the store as soon as the call chain awaits anything (e.g.\n * the inner `customSession` → `getSession()` call). All endpoints that\n * read request-state via `defineRequestState()` then throw\n * \"No request state found\".\n *\n * Single-flight caveat:\n * - This polyfill is process-global, not async-context-local. In a real\n * server it could leak state across concurrent requests. That risk is\n * acceptable here because:\n * 1) It is only installed when WebContainer is detected (dev / preview\n * sandboxes that handle one request at a time).\n * 2) Each request still wraps the entire handler in `runWithRequestState`\n * with a fresh WeakMap, so the in-flight request always sees its own\n * store as long as nothing else mutates the slot mid-flight.\n */\nclass WebContainerRequestStateAsyncLocalStorage<T> {\n private current: T | undefined = undefined;\n\n run<R>(store: T, fn: () => R): R {\n const prev = this.current;\n this.current = store;\n try {\n const result = fn() as unknown;\n if (result && typeof (result as Promise<unknown>).then === 'function') {\n return (result as Promise<unknown>).finally(() => {\n this.current = prev;\n }) as unknown as R;\n }\n this.current = prev;\n return result as R;\n } catch (err) {\n this.current = prev;\n throw err;\n }\n }\n\n getStore(): T | undefined {\n return this.current;\n }\n}\n\n/**\n * Pre-populate better-auth's global `requestStateAsyncStorage` slot with the\n * synchronous polyfill when running inside WebContainer.\n *\n * Better-auth caches its AsyncLocalStorage instance on\n * `globalThis[Symbol.for('better-auth:global')].context.requestStateAsyncStorage`\n * the first time `ensureAsyncStorage()` runs (see\n * `@better-auth/core/dist/context/request-state.mjs`). By seeding that slot\n * BEFORE any better-auth code touches it, every call to\n * `runWithRequestState` / `getCurrentRequestState` — including the\n * `@better-auth/core` copy that `plugin-auth` imports directly and the copy\n * bundled with `better-auth` itself — share the same working polyfill.\n *\n * Outside WebContainer this is a no-op so production deployments keep\n * Node's native AsyncLocalStorage.\n */\nfunction installWebContainerRequestStatePolyfill(): void {\n if (!isWebContainerRuntime()) return;\n const sym = Symbol.for('better-auth:global');\n const g = globalThis as any;\n if (!g[sym]) {\n g[sym] = { version: '0.0.0-polyfill', epoch: 0, context: {} };\n }\n if (!g[sym].context) g[sym].context = {};\n if (!g[sym].context.requestStateAsyncStorage) {\n g[sym].context.requestStateAsyncStorage = new WebContainerRequestStateAsyncLocalStorage();\n // eslint-disable-next-line no-console\n console.warn(\n '[AuthManager] WebContainer detected: installed synchronous request-state polyfill ' +\n '(node:async_hooks AsyncLocalStorage does not propagate context across await in WebContainer).',\n );\n }\n}\n\n/**\n * Extended options for AuthManager\n */\nexport interface AuthManagerOptions extends Partial<AuthConfig> {\n /**\n * Better-Auth instance (for advanced use cases)\n * If not provided, one will be created from config\n */\n authInstance?: Auth<any>;\n\n /**\n * ObjectQL Data Engine instance\n * Required for database operations using ObjectQL instead of third-party ORMs\n */\n dataEngine?: IDataEngine;\n\n /**\n * Base path for auth routes\n * Forwarded to better-auth's basePath option so it can match incoming\n * request URLs without manual path rewriting.\n * @default '/api/v1/auth'\n */\n basePath?: string;\n\n /**\n * OIDC / Generic OAuth2 providers for enterprise SSO.\n * Each entry is passed to better-auth's genericOAuth plugin.\n */\n oidcProviders?: OidcProvidersConfig;\n\n /**\n * Application-specific organization roles to register with Better-Auth's\n * organization plugin. Each name becomes a valid role for invitations and\n * member assignments without going through Better-Auth's default\n * `owner|admin|member` whitelist.\n *\n * The ObjectStack SecurityPlugin handles real RBAC enforcement by matching\n * these role names against `permission` metadata (PermissionSets / Profiles),\n * so Better-Auth only needs to accept them as opaque strings. Each role is\n * registered with the minimum access-control privileges (equivalent to\n * Better-Auth's `member` role) so it cannot inadvertently grant org-level\n * admin capabilities.\n *\n * Typical source: the union of `permission` metadata names that have\n * `isProfile: true`, collected from the loaded stack at CLI boot.\n *\n * @example ['sales_rep', 'sales_manager', 'service_agent']\n */\n additionalOrgRoles?: string[];\n\n /**\n * Optional outbound email service used by better-auth callbacks\n * (`sendResetPassword`, `sendVerificationEmail`, `sendInvitationEmail`,\n * `sendMagicLink`). When omitted, those callbacks degrade to logging\n * the action URL — keeping flows usable in pilots / local dev — but\n * production deployments SHOULD wire one via `setEmailService()`.\n *\n * Resolved lazily through {@link AuthManager.getEmailService}; safe\n * to set after construction. AuthPlugin wires this from the kernel\n * service registry on `kernel:ready`.\n */\n emailService?: IEmailService;\n\n /**\n * Display name used by built-in auth email templates (`{{appName}}`\n * placeholder). Defaults to `'ObjectStack'` when omitted.\n */\n appName?: string;\n\n /**\n * Pass-through to better-auth's `databaseHooks` option. better-auth fires\n * these around its own adapter writes (e.g. when `genericOAuth` creates\n * a JIT user during SSO login), which the kernel-level ObjectQL\n * middleware does NOT observe — better-auth's adapter goes through\n * `dataEngine` directly, bypassing the `ql.registerMiddleware` chain.\n *\n * The platform uses this to attach a `user.create.after` hook that\n * auto-provisions a personal organization for every newly-created user\n * (mirroring what SecurityPlugin's middleware does for direct\n * ObjectQL inserts) so SSO-arriving users don't land on the empty\n * \"create organization\" screen.\n */\n databaseHooks?: BetterAuthOptions['databaseHooks'];\n}\n\n/**\n * Authentication Manager\n *\n * Wraps better-auth and provides authentication services for ObjectStack.\n * Supports multiple authentication methods:\n * - Email/password\n * - OAuth providers (Google, GitHub, etc.)\n * - Magic links\n * - Two-factor authentication\n * - Passkeys\n * - Organization/teams\n */\nexport class AuthManager {\n private auth: Auth<any> | null = null;\n private config: AuthManagerOptions;\n\n constructor(config: AuthManagerOptions) {\n this.config = config;\n\n // WebContainer (StackBlitz) compatibility — install a synchronous\n // AsyncLocalStorage polyfill for better-auth's request-state global\n // BEFORE better-auth ever instantiates its own. See the helper for the\n // full rationale.\n installWebContainerRequestStatePolyfill();\n\n // Use provided auth instance\n if (config.authInstance) {\n this.auth = config.authInstance;\n }\n // Don't create auth instance automatically to avoid database initialization errors\n // It will be created lazily when needed\n }\n\n /**\n * Get or create the better-auth instance (lazy initialization)\n */\n private async getOrCreateAuth(): Promise<Auth<any>> {\n if (!this.auth) {\n this.auth = await this.createAuthInstance();\n }\n return this.auth;\n }\n\n /**\n * Create a better-auth instance from configuration\n */\n private async createAuthInstance(): Promise<Auth<any>> {\n const { betterAuth } = await import('better-auth');\n const plugins = await this.buildPluginList();\n const passwordHasher = await this.resolvePasswordHasher();\n const betterAuthConfig: BetterAuthOptions = {\n // Base configuration\n secret: this.config.secret || this.generateSecret(),\n baseURL: this.config.baseUrl || 'http://localhost:3000',\n basePath: this.config.basePath || '/api/v1/auth',\n\n // Database adapter configuration\n database: this.createDatabaseConfig(),\n\n // Model/field mapping: camelCase (better-auth) → snake_case (ObjectStack)\n // These declarations tell better-auth the actual table/column names used\n // by ObjectStack's protocol layer, enabling automatic transformation via\n // createAdapterFactory.\n user: {\n ...AUTH_USER_CONFIG,\n },\n account: {\n ...AUTH_ACCOUNT_CONFIG,\n // Allow OIDC/OAuth callbacks to implicitly link the incoming\n // identity to a pre-existing local user when the emails match.\n //\n // ObjectStack's platform SSO (\"objectstack-cloud\" provider) is the\n // canonical case: cloud is the IdP for every project, so a user\n // arriving via SSO is — by construction — the same person who was\n // auto-seeded as the project owner when the project was created.\n // Without trusting the provider, better-auth's safety check rejects\n // the link with `error=account_not_linked` because the seeded user\n // row has `emailVerified=false` (no actual verification ever runs\n // in the IdP-mediated flow). See packages/plugins/plugin-auth/\n // node_modules/better-auth/dist/oauth2/link-account.mjs:22.\n //\n // Custom-deployment consumers can extend the trusted set via\n // `config.account.accountLinking.trustedProviders`; we always\n // include `objectstack-cloud` because it is the platform IdP.\n accountLinking: {\n enabled: true,\n // better-auth's account-linking gate has TWO independent clauses\n // (see link-account.mjs:22). Trusting the provider only satisfies\n // the first clause; the second — `requireLocalEmailVerified &&\n // !dbUser.user.emailVerified` — still blocks linking when the\n // pre-existing local user row has `emailVerified=false` (the\n // default for owner-seeded rows). Disabling the local-email gate\n // is safe here because the OAuth side is what we actually trust:\n // the incoming identity was verified by the IdP. Consumers who\n // need the stricter behavior can override via config.\n requireLocalEmailVerified: false,\n ...((this.config as any)?.account?.accountLinking ?? {}),\n trustedProviders: Array.from(new Set([\n 'objectstack-cloud',\n ...((this.config as any)?.account?.accountLinking?.trustedProviders ?? []),\n ])),\n },\n },\n verification: {\n ...AUTH_VERIFICATION_CONFIG,\n },\n\n // Social / OAuth providers\n ...(this.config.socialProviders ? { socialProviders: this.config.socialProviders as any } : {}),\n\n // Email and password configuration\n emailAndPassword: {\n enabled: this.config.emailAndPassword?.enabled ?? true,\n ...(passwordHasher ? { password: passwordHasher } : {}),\n ...(this.config.emailAndPassword?.disableSignUp != null\n ? { disableSignUp: this.config.emailAndPassword.disableSignUp } : {}),\n ...(this.config.emailAndPassword?.requireEmailVerification != null\n ? { requireEmailVerification: this.config.emailAndPassword.requireEmailVerification } : {}),\n ...(this.config.emailAndPassword?.minPasswordLength != null\n ? { minPasswordLength: this.config.emailAndPassword.minPasswordLength } : {}),\n ...(this.config.emailAndPassword?.maxPasswordLength != null\n ? { maxPasswordLength: this.config.emailAndPassword.maxPasswordLength } : {}),\n ...(this.config.emailAndPassword?.resetPasswordTokenExpiresIn != null\n ? { resetPasswordTokenExpiresIn: this.config.emailAndPassword.resetPasswordTokenExpiresIn } : {}),\n ...(this.config.emailAndPassword?.autoSignIn != null\n ? { autoSignIn: this.config.emailAndPassword.autoSignIn } : {}),\n ...(this.config.emailAndPassword?.revokeSessionsOnPasswordReset != null\n ? { revokeSessionsOnPasswordReset: this.config.emailAndPassword.revokeSessionsOnPasswordReset } : {}),\n sendResetPassword: async ({ user, url, token }: { user: { id: string; email: string; name?: string }; url: string; token: string }) => {\n const email = this.getEmailService();\n if (!email) {\n console.warn(\n `[AuthManager] Password-reset requested for ${user.email} but no email service is wired. URL: ${url}`,\n );\n return;\n }\n const ttlSec = this.config.emailAndPassword?.resetPasswordTokenExpiresIn ?? 60 * 60;\n try {\n await email.sendTemplate({\n template: 'auth.password_reset',\n to: { address: user.email, ...(user.name ? { name: user.name } : {}) },\n data: {\n user: { name: user.name || user.email, email: user.email, id: user.id },\n resetUrl: url,\n token,\n expiresInMinutes: Math.round(ttlSec / 60),\n appName: this.getAppName(),\n },\n relatedObject: 'sys_user',\n relatedId: user.id,\n });\n } catch (err: any) {\n console.error(`[AuthManager] sendResetPassword failed: ${err?.message ?? err}`);\n throw err;\n }\n },\n },\n\n // Email verification\n ...(this.config.emailVerification || this.config.emailService ? {\n emailVerification: {\n ...(this.config.emailVerification?.sendOnSignUp != null\n ? { sendOnSignUp: this.config.emailVerification.sendOnSignUp } : {}),\n ...(this.config.emailVerification?.sendOnSignIn != null\n ? { sendOnSignIn: this.config.emailVerification.sendOnSignIn } : {}),\n ...(this.config.emailVerification?.autoSignInAfterVerification != null\n ? { autoSignInAfterVerification: this.config.emailVerification.autoSignInAfterVerification } : {}),\n ...(this.config.emailVerification?.expiresIn != null\n ? { expiresIn: this.config.emailVerification.expiresIn } : {}),\n sendVerificationEmail: async ({ user, url, token }: { user: { id: string; email: string; name?: string }; url: string; token: string }) => {\n const email = this.getEmailService();\n if (!email) {\n console.warn(\n `[AuthManager] Verification email requested for ${user.email} but no email service is wired. URL: ${url}`,\n );\n return;\n }\n const ttlSec = this.config.emailVerification?.expiresIn ?? 60 * 60;\n try {\n await email.sendTemplate({\n template: 'auth.verify_email',\n to: { address: user.email, ...(user.name ? { name: user.name } : {}) },\n data: {\n user: { name: user.name || user.email, email: user.email, id: user.id },\n verificationUrl: url,\n token,\n expiresInMinutes: Math.round(ttlSec / 60),\n appName: this.getAppName(),\n },\n relatedObject: 'sys_user',\n relatedId: user.id,\n });\n } catch (err: any) {\n console.error(`[AuthManager] sendVerificationEmail failed: ${err?.message ?? err}`);\n throw err;\n }\n },\n },\n } : {}),\n\n // Session configuration\n session: {\n ...AUTH_SESSION_CONFIG,\n expiresIn: this.config.session?.expiresIn || 60 * 60 * 24 * 7, // 7 days default\n updateAge: this.config.session?.updateAge || 60 * 60 * 24, // 1 day default\n },\n\n // better-auth plugins — registered based on AuthPluginConfig flags\n plugins,\n\n // Database hooks (fired by better-auth's adapter writes — these run\n // for SSO JIT-provisioning too, unlike kernel-level ObjectQL\n // middleware which better-auth's adapter bypasses).\n ...(this.config.databaseHooks ? { databaseHooks: this.config.databaseHooks } : {}),\n\n // Trusted origins for CSRF protection (supports wildcards like \"https://*.example.com\")\n // Auto-includes origins from CORS_ORIGIN env var so CORS and CSRF stay in sync.\n ...(() => {\n const origins: string[] = [...(this.config.trustedOrigins || [])];\n // Sync with CORS_ORIGIN env var (comma-separated)\n const corsOrigin = process.env.CORS_ORIGIN;\n if (corsOrigin && corsOrigin !== '*') {\n corsOrigin.split(',').map(s => s.trim()).filter(Boolean).forEach(o => {\n if (!origins.includes(o)) origins.push(o);\n });\n }\n // When CORS allows all origins (default) and no explicit trustedOrigins,\n // trust all localhost ports in development for convenience. Also trust\n // `*.localhost` subdomains so per-project tenant subdomains (the dev\n // default root domain — see project-provisioning.ts) pass CSRF checks\n // without operators having to configure trustedOrigins manually.\n if (!origins.length && (!corsOrigin || corsOrigin === '*')) {\n origins.push('http://localhost:*');\n origins.push('http://*.localhost:*');\n origins.push('https://*.localhost:*');\n }\n return origins.length ? { trustedOrigins: origins } : {};\n })(),\n\n // Advanced options (cross-subdomain cookies, secure cookies, CSRF, etc.)\n ...(this.config.advanced ? {\n advanced: {\n ...(this.config.advanced.crossSubDomainCookies\n ? { crossSubDomainCookies: this.config.advanced.crossSubDomainCookies } : {}),\n ...(this.config.advanced.useSecureCookies != null\n ? { useSecureCookies: this.config.advanced.useSecureCookies } : {}),\n ...(this.config.advanced.disableCSRFCheck != null\n ? { disableCSRFCheck: this.config.advanced.disableCSRFCheck } : {}),\n ...(this.config.advanced.cookiePrefix != null\n ? { cookiePrefix: this.config.advanced.cookiePrefix } : {}),\n },\n } : {}),\n };\n\n return betterAuth(betterAuthConfig);\n }\n\n /**\n * Detect WebContainer (StackBlitz) and swap in a pure-JS scrypt hasher.\n *\n * better-auth defaults to `@better-auth/utils/password.node`, which calls\n * `node:crypto.scrypt`. WebContainer polyfills that API incompletely and\n * signup throws `TypeError: y.run is not a function`.\n *\n * We can't dynamic-import `@better-auth/utils/password` because that\n * package's `exports` map gates the pure-JS build behind a non-`\"node\"`\n * condition — Node-the-runtime (which WebContainer reports itself as)\n * always resolves to `password.node.mjs`. So we reimplement the same hash\n * here using `@noble/hashes/scrypt` directly, with byte-identical params\n * (N=16384, r=16, p=1, dkLen=64) and the same `{saltHex}:{keyHex}` storage\n * format. Hashes produced by either implementation verify against the\n * other — no migration needed.\n *\n * Returns `undefined` outside WebContainer so production deployments keep\n * the native (fast) hasher and never load `@noble/hashes`.\n */\n private async resolvePasswordHasher(): Promise<\n { hash: (password: string) => Promise<string>; verify: (args: { hash: string; password: string }) => Promise<boolean> } | undefined\n > {\n if (!isWebContainerRuntime()) return undefined;\n try {\n const { scryptAsync } = await import('@noble/hashes/scrypt.js');\n const PARAMS = { N: 16384, r: 16, p: 1, dkLen: 64, maxmem: 128 * 16384 * 16 * 2 } as const;\n const toHex = (b: Uint8Array): string => {\n let s = '';\n for (let i = 0; i < b.length; i++) s += b[i]!.toString(16).padStart(2, '0');\n return s;\n };\n const generateKey = (password: string, saltHex: string): Promise<Uint8Array> =>\n scryptAsync(password.normalize('NFKC'), saltHex, PARAMS);\n return {\n hash: async (password: string) => {\n const saltBytes = (globalThis as any).crypto.getRandomValues(new Uint8Array(16));\n const saltHex = toHex(saltBytes);\n const key = await generateKey(password, saltHex);\n return `${saltHex}:${toHex(key)}`;\n },\n verify: async ({ hash, password }) => {\n const [saltHex, keyHex] = hash.split(':');\n if (!saltHex || !keyHex) throw new Error('Invalid password hash');\n const target = await generateKey(password, saltHex);\n return toHex(target) === keyHex;\n },\n };\n } catch (err: any) {\n console.warn(\n `[AuthManager] WebContainer detected but pure-JS scrypt unavailable: ${err?.message ?? err}. Falling back to default.`,\n );\n return undefined;\n }\n }\n\n /**\n * Build the list of better-auth plugins based on AuthPluginConfig flags.\n *\n * Each plugin that introduces its own database tables is configured with\n * a `schema` option containing the appropriate snake_case field mappings,\n * so that `createAdapterFactory` transforms them automatically.\n */\n private async buildPluginList(): Promise<any[]> {\n const pluginConfig: Partial<AuthPluginConfig> = this.config.plugins ?? {};\n const plugins: any[] = [];\n\n // Defaults — kept in sync with `AuthPluginConfigSchema` in\n // @objectstack/spec/system/auth-config.zod.ts. The frontend AuthProvider\n // (in @object-ui/app-shell) calls `/api/v1/auth/organization/list` on\n // every load; making the org plugin opt-out (default true) avoids\n // 404s and the noisy \"Failed to load organizations\" warning.\n const enabled = {\n organization: pluginConfig.organization ?? true,\n twoFactor: pluginConfig.twoFactor ?? false,\n passkeys: pluginConfig.passkeys ?? false,\n magicLink: pluginConfig.magicLink ?? false,\n oidcProvider: pluginConfig.oidcProvider ?? false,\n deviceAuthorization: pluginConfig.deviceAuthorization ?? false,\n admin: pluginConfig.admin ?? false,\n };\n\n // bearer() — ALWAYS enabled.\n //\n // Enables token-based authentication for cross-origin and mobile clients\n // where third-party cookies are blocked (e.g. Safari ITP, Chrome CHIPS,\n // native apps). The plugin:\n // • Accepts `Authorization: Bearer <token>` on incoming requests and\n // transparently resolves the session as if a cookie had been sent.\n // • Emits a `set-auth-token` response header on sign-in / session-refresh\n // that the client can store (e.g. in `localStorage`) and replay on\n // subsequent requests.\n //\n // This mirrors how Salesforce, Notion, Supabase and first-party mobile\n // SDKs handle auth. Cookie-based auth remains available for same-origin\n // browser deployments; bearer is additive, not a replacement.\n const { bearer } = await import('better-auth/plugins/bearer');\n plugins.push(bearer());\n\n if (enabled.organization) {\n const { organization } = await import('better-auth/plugins/organization');\n // Build a `roles` map that registers each app-supplied org role\n // (e.g. CRM's sales_rep, sales_manager) as a valid Better-Auth role\n // so invitations to those roles aren't rejected with ROLE_NOT_FOUND.\n // Real RBAC enforcement is handled by ObjectStack's SecurityPlugin,\n // which matches the role name against `permission` metadata\n // (PermissionSets). Here we register them with minimum org-plugin\n // capabilities (same as the built-in `member` role) so they cannot\n // inadvertently grant org-level admin powers.\n let customOrgRoles: Record<string, any> | undefined;\n const extra = this.config.additionalOrgRoles;\n if (extra && extra.length > 0) {\n try {\n const accessMod = await import('better-auth/plugins/organization/access');\n const { defaultAc, memberAc, defaultRoles: importedDefaultRoles } = accessMod as any;\n // Better-Auth's `hasPermission` does `{...options.roles || defaultRoles}`\n // (precedence: `||` then spread). When we pass our own `roles`, the\n // built-in owner/admin/member are silently dropped, so even the org\n // owner loses `invitation:create` and every mutation 403s. We must\n // re-include the defaults alongside our extras.\n const defaultRoles = importedDefaultRoles || null;\n if (defaultAc && memberAc && typeof memberAc.statements === 'object') {\n const built: Record<string, any> = defaultRoles ? { ...defaultRoles } : {};\n const stmts = memberAc.statements;\n for (const name of extra) {\n if (!name) continue;\n if (built[name]) continue;\n built[name] = defaultAc.newRole(stmts);\n }\n customOrgRoles = built;\n }\n } catch {\n customOrgRoles = undefined;\n }\n }\n plugins.push(organization({\n schema: buildOrganizationPluginSchema(),\n // Enable the team sub-feature so the framework's `sys_team` /\n // `sys_team_member` tables (already declared in platform-objects)\n // are actually wired up to better-auth's CRUD endpoints\n // (`/organization/{create,update,remove,list}-team[s]` and\n // `/organization/{add,remove,list}-team-member[s]`). The Account\n // portal exposes a Teams page; without this flag those endpoints\n // 404 and the section silently breaks.\n teams: { enabled: true },\n // Without a mailer wired in framework, requiring email verification\n // before accepting invitations dead-ends every invite flow with\n // FORBIDDEN EMAIL_VERIFICATION_REQUIRED…. Default-off here keeps\n // the built-in /accept-invitation route usable for pilots; operators\n // who wire a real mailer can re-enable downstream.\n requireEmailVerificationOnInvitation: false,\n ...(customOrgRoles ? { roles: customOrgRoles } : {}),\n // ── Slug-change guard ─────────────────────────────────────\n // An org's slug is baked into every env hostname at creation\n // time (see service-tenant `project-provisioning.ts`). Renaming\n // it while live envs exist would silently desync the URL from\n // the org identity. Block the change here; the cloud Console\n // surfaces this as an actionable error and points users to\n // `change_hostname` or archiving the env. Org `name` (display\n // label) is unaffected — only `slug` is guarded.\n //\n // We resolve the data engine lazily so non-cloud apps (which\n // never seed `sys_environment`) keep working: any lookup error\n // is treated as \"no envs to protect\".\n organizationHooks: {\n // Gate fresh organization creation behind `OS_MULTI_ORG_ENABLED`.\n // The plugin itself is always installed (so list/update/invite endpoints\n // keep responding); only the `create` operation is denied when the\n // deployment is provisioned in single-org mode. Default is enabled\n // to preserve historical behaviour.\n beforeCreateOrganization: async () => {\n const flag = String(\n (globalThis as any)?.process?.env?.OS_MULTI_ORG_ENABLED ?? 'true',\n ).toLowerCase();\n if (flag === 'false') {\n const { APIError } = await import('better-auth/api');\n throw new APIError('FORBIDDEN', {\n message:\n 'Creating additional organizations is disabled on this deployment.',\n });\n }\n },\n beforeUpdateOrganization: async ({ organization, member }: any) => {\n const newSlug = organization?.slug;\n const orgId = member?.organizationId;\n if (!newSlug || !orgId) return;\n\n const dataEngine = this.config.dataEngine as any;\n if (!dataEngine) return;\n\n let currentSlug: string | undefined;\n try {\n const current = await dataEngine.findOne('sys_organization', {\n where: { id: orgId },\n });\n currentSlug = current?.slug;\n } catch {\n return;\n }\n if (!currentSlug || currentSlug === newSlug) return;\n\n let activeEnvs = 0;\n try {\n const envs = await dataEngine.find('sys_environment', {\n where: { organization_id: orgId },\n });\n activeEnvs = (envs ?? []).filter(\n (e: any) => e?.status !== 'archived' && e?.status !== 'failed',\n ).length;\n } catch {\n return;\n }\n\n if (activeEnvs > 0) {\n const { APIError } = await import('better-auth/api');\n throw new APIError('FORBIDDEN', {\n message:\n `Cannot change organization slug while ${activeEnvs} active ` +\n `environment(s) still reference it. Archive those environments ` +\n `or rename their hostnames first.`,\n });\n }\n },\n },\n // No mailer is wired in framework yet — log the accept URL so\n // operators / UI can fall back to copy-paste flows. Replace this\n // with a real mail integration when available.\n sendInvitationEmail: async ({ email: recipientEmail, invitation, organization: org, inviter }) => {\n const baseUrl = (this.config.baseUrl ?? '').replace(/\\/$/, '');\n const acceptUrl = `${baseUrl}/accept-invitation/${invitation.id}`;\n const emailService = this.getEmailService();\n if (!emailService) {\n console.warn(\n `[AuthManager] Invitation email not configured. ` +\n `To: ${recipientEmail} (org: ${org?.name ?? invitation.organizationId}, ` +\n `role: ${invitation.role}, inviter: ${inviter?.user?.email ?? 'unknown'}) ` +\n `URL: ${acceptUrl}`,\n );\n return;\n }\n try {\n await emailService.sendTemplate({\n template: 'auth.invitation',\n to: recipientEmail,\n data: {\n inviter: {\n name: inviter?.user?.name ?? inviter?.user?.email ?? 'A teammate',\n email: inviter?.user?.email ?? '',\n },\n organization: { name: org?.name ?? invitation.organizationId },\n role: invitation.role || '',\n acceptUrl,\n appName: this.getAppName(),\n },\n relatedObject: 'sys_invitation',\n relatedId: invitation.id,\n });\n } catch (err: any) {\n console.error(`[AuthManager] sendInvitationEmail failed: ${err?.message ?? err}`);\n throw err;\n }\n },\n }));\n }\n\n if (enabled.twoFactor) {\n const { twoFactor } = await import('better-auth/plugins/two-factor');\n plugins.push(twoFactor({\n schema: buildTwoFactorPluginSchema(),\n }));\n }\n\n if (enabled.admin) {\n const { admin } = await import('better-auth/plugins/admin');\n // Platform admin: ban/unban, set-password, impersonate, set-role.\n // Schema mapping ensures the plugin's added user/session columns\n // match ObjectStack's snake_case conventions (ban_reason,\n // ban_expires, impersonated_by). `role` and `banned` are already\n // snake_case-compatible.\n plugins.push(admin({\n schema: buildAdminPluginSchema(),\n }));\n }\n\n if (enabled.magicLink) {\n const { magicLink } = await import('better-auth/plugins/magic-link');\n // magic-link reuses the `verification` table — no extra schema mapping needed.\n plugins.push(magicLink({\n sendMagicLink: async ({ email: recipientEmail, url, token }) => {\n const emailService = this.getEmailService();\n if (!emailService) {\n console.warn(\n `[AuthManager] Magic-link requested for ${recipientEmail} but no email service is wired. URL: ${url}`,\n );\n return;\n }\n try {\n await emailService.sendTemplate({\n template: 'auth.magic_link',\n to: recipientEmail,\n data: {\n magicLinkUrl: url,\n token,\n expiresInMinutes: 10,\n appName: this.getAppName(),\n },\n });\n } catch (err: any) {\n console.error(`[AuthManager] sendMagicLink failed: ${err?.message ?? err}`);\n throw err;\n }\n },\n }));\n }\n\n // OIDC / Generic OAuth2 providers (enterprise SSO via genericOAuth plugin)\n if (this.config.oidcProviders?.length) {\n const { genericOAuth } = await import('better-auth/plugins/generic-oauth');\n plugins.push(genericOAuth({\n config: this.config.oidcProviders.map(p => ({\n providerId: p.providerId,\n ...(p.discoveryUrl ? { discoveryUrl: p.discoveryUrl } : {}),\n ...(p.issuer ? { issuer: p.issuer } : {}),\n ...(p.authorizationUrl ? { authorizationUrl: p.authorizationUrl } : {}),\n ...(p.tokenUrl ? { tokenUrl: p.tokenUrl } : {}),\n ...(p.userInfoUrl ? { userInfoUrl: p.userInfoUrl } : {}),\n clientId: p.clientId,\n clientSecret: p.clientSecret,\n ...(p.scopes ? { scopes: p.scopes } : {}),\n ...(p.pkce != null ? { pkce: p.pkce } : {}),\n })),\n }));\n }\n\n // OAuth/OIDC Provider — turn this server into an OpenID Connect Identity\n // Provider so external apps can SSO via ObjectStack. Adds the\n // `/oauth2/{authorize,token,userinfo,register,consent,endsession}` and\n // `/.well-known/openid-configuration` endpoints under the auth route.\n //\n // Migrated from the deprecated `better-auth/plugins/oidc-provider` to the\n // standalone `@better-auth/oauth-provider` package. The new plugin uses\n // `oauthClient`, `oauthAccessToken`, `oauthRefreshToken`, and `oauthConsent`\n // models — see `buildOauthProviderPluginSchema()` for the snake_case\n // mappings to ObjectStack's `sys_oauth_*` tables.\n if (enabled.oidcProvider) {\n // The new @better-auth/oauth-provider package requires the `jwt`\n // plugin (used to sign id_tokens / JWT access tokens). Register it\n // automatically — it is otherwise an internal implementation detail\n // and forcing every consumer to opt in would be poor DX.\n const { jwt } = await import('better-auth/plugins');\n plugins.push(jwt({ schema: buildJwtPluginSchema() }));\n\n const { oauthProvider } = await import('@better-auth/oauth-provider');\n const baseUrl = (this.config.baseUrl ?? '').replace(/\\/$/, '');\n plugins.push(oauthProvider({\n // Account SPA renders both pages — see apps/account.\n loginPage: `${baseUrl}/_account/login`,\n consentPage: `${baseUrl}/_account/oauth/consent`,\n schema: buildOauthProviderPluginSchema(),\n }));\n }\n\n // Device Authorization Grant (RFC 8628) — for CLI / TV-style devices.\n // Exposes the standard `/device/{code,token,approve,deny}` endpoints\n // and persists pending requests in `sys_device_code`.\n //\n // The verification URI points at the account portal page that lets a\n // signed-in user approve or deny a pending CLI login. The page reads\n // the `user_code` query parameter that better-auth appends to\n // `verification_uri_complete`.\n if (enabled.deviceAuthorization) {\n const { deviceAuthorization } = await import('better-auth/plugins/device-authorization');\n const baseUrl = (this.config.baseUrl ?? '').replace(/\\/$/, '');\n plugins.push(deviceAuthorization({\n verificationUri: `${baseUrl}/_account/auth/device`,\n schema: buildDeviceAuthorizationPluginSchema(),\n }));\n }\n\n // customSession() — augments the session payload with a derived `role`\n // field so frontend gating (e.g. AppShell's `isAdmin = user.role === 'admin'`)\n // works without each consumer having to re-query permission sets.\n //\n // Better-auth's `sys_user` table doesn't carry a `role` column. We derive\n // it from two sources:\n //\n // 1. **Platform admin** — a `sys_user_permission_set` row that points at\n // the `admin_full_access` permission set with `organization_id = null`\n // (seeded by `bootstrapPlatformAdmin`).\n // 2. **Organization admin** — a `sys_member` row in the user's *active*\n // organization (`session.activeOrganizationId`) with role `owner` or\n // `admin`. Org owners/admins are entitled to manage org-scoped\n // metadata such as saved list views, dashboards, etc.\n //\n // Either path synthesizes `user.role = 'admin'` so the frontend can gate\n // metadata-edit affordances uniformly. The raw membership role remains\n // available via the `organization` plugin's `member` payload for\n // finer-grained checks.\n const dataEngine = this.config.dataEngine;\n if (dataEngine) {\n const { customSession } = await import('better-auth/plugins/custom-session');\n plugins.push(customSession(async ({ user, session }) => {\n if (!user?.id) return { user, session };\n\n const isPlatformAdmin = async (): Promise<boolean> => {\n try {\n const links = await dataEngine.find('sys_user_permission_set', {\n where: { user_id: user.id },\n limit: 50,\n });\n const platformLinks = (Array.isArray(links) ? links : []).filter(\n (l: any) => !l.organization_id,\n );\n if (platformLinks.length === 0) return false;\n const sets = await dataEngine.find('sys_permission_set', { limit: 50 });\n const adminSet = (Array.isArray(sets) ? sets : []).find(\n (r: any) => r.name === 'admin_full_access',\n );\n if (!adminSet) return false;\n return platformLinks.some(\n (l: any) => l.permission_set_id === adminSet.id,\n );\n } catch {\n return false;\n }\n };\n\n const isActiveOrgAdmin = async (): Promise<boolean> => {\n try {\n const orgId = (session as any)?.activeOrganizationId;\n if (!orgId) return false;\n const members = await dataEngine.find('sys_member', {\n where: { user_id: user.id, organization_id: orgId },\n limit: 5,\n });\n return (Array.isArray(members) ? members : []).some((m: any) => {\n // better-auth org plugin stores roles as either a single string\n // or a comma-separated list (e.g. `\"owner,admin\"`). Treat any\n // membership that includes `owner` or `admin` as administrative.\n const raw = typeof m?.role === 'string' ? m.role : '';\n const roles = raw.split(',').map((s: string) => s.trim().toLowerCase());\n return roles.includes('owner') || roles.includes('admin');\n });\n } catch {\n return false;\n }\n };\n\n const promote = (await isPlatformAdmin()) || (await isActiveOrgAdmin());\n if (!promote) return { user, session };\n return { user: { ...user, role: 'admin' }, session };\n }));\n }\n\n return plugins;\n }\n\n /**\n * Create database configuration using ObjectQL adapter\n *\n * better-auth resolves the `database` option as follows:\n * - `undefined` → in-memory adapter\n * - `typeof fn === \"function\"` → treated as `DBAdapterInstance`, called with `(options)`\n * - otherwise → forwarded to Kysely adapter factory (pool/dialect)\n *\n * A raw `CustomAdapter` object would fall into the third branch and fail\n * silently. We therefore wrap the ObjectQL adapter in a factory function\n * so it is correctly recognised as a `DBAdapterInstance`.\n */\n private createDatabaseConfig(): any {\n // Use ObjectQL adapter factory if dataEngine is provided\n if (this.config.dataEngine) {\n // createObjectQLAdapterFactory returns an AdapterFactory\n // (options => DBAdapter) which better-auth invokes via getBaseAdapter().\n // The factory is created by better-auth's createAdapterFactory and\n // automatically applies modelName/fields transformations declared in\n // the betterAuth config above.\n return createObjectQLAdapterFactory(this.config.dataEngine);\n }\n\n // Fallback warning if no dataEngine is provided\n console.warn(\n '⚠️ WARNING: No dataEngine provided to AuthManager! ' +\n 'Using in-memory storage. This is NOT suitable for production. ' +\n 'Please provide a dataEngine instance (e.g., ObjectQL) in AuthManagerOptions.'\n );\n\n // Return a minimal in-memory configuration as fallback\n // This allows the system to work in development/testing without a real database\n return undefined; // better-auth will use its default in-memory adapter\n }\n\n /**\n * Generate a secure secret if not provided\n */\n private generateSecret(): string {\n const envSecret = process.env.AUTH_SECRET;\n\n if (!envSecret) {\n // In production, a secret MUST be provided\n // For development/testing, we'll use a fallback but warn about it\n const fallbackSecret = 'dev-secret-' + Date.now();\n\n console.warn(\n '⚠️ WARNING: No AUTH_SECRET environment variable set! ' +\n 'Using a temporary development secret. ' +\n 'This is NOT secure for production use. ' +\n 'Please set AUTH_SECRET in your environment variables.'\n );\n\n return fallbackSecret;\n }\n\n return envSecret;\n }\n\n /**\n * Update the base URL at runtime.\n *\n * This **must** be called before the first request triggers lazy\n * initialisation of the better-auth instance — typically from a\n * `kernel:ready` hook where the actual server port is known.\n *\n * If the auth instance has already been created this is a no-op and\n * a warning is emitted.\n */\n setRuntimeBaseUrl(url: string): void {\n if (this.auth) {\n console.warn(\n '[AuthManager] setRuntimeBaseUrl() called after the auth instance was already created — ignoring. ' +\n 'Ensure this method is called before the first request.',\n );\n return;\n }\n this.config = { ...this.config, baseUrl: url };\n }\n\n /**\n * Inject (or replace) the outbound email service used by better-auth\n * callbacks. Safe to call after construction but BEFORE the first\n * request hits the auth handler — callbacks read this via\n * {@link getEmailService} when invoked.\n *\n * AuthPlugin calls this on `kernel:ready` once `ctx.getService('email')`\n * resolves. For tests / serverless, callers may invoke directly.\n */\n setEmailService(email: IEmailService | undefined): void {\n this.config.emailService = email;\n }\n\n /** @internal Used by callback closures. */\n private getEmailService(): IEmailService | undefined {\n return this.config.emailService;\n }\n\n /** @internal `{{appName}}` placeholder value for built-in templates. */\n private getAppName(): string {\n return this.config.appName ?? 'ObjectStack';\n }\n\n /**\n * Get the underlying better-auth instance\n * Useful for advanced use cases\n */\n getAuthInstance(): Promise<Auth<any>> {\n return this.getOrCreateAuth();\n }\n\n /**\n * Handle an authentication request\n * Forwards the request directly to better-auth's universal handler\n *\n * better-auth catches internal errors (database / adapter / ORM) and\n * returns a 500 Response instead of throwing. We therefore inspect the\n * response status and log server errors so they are not silently swallowed.\n *\n * @param request - Web standard Request object\n * @returns Web standard Response object\n */\n async handleRequest(request: Request): Promise<Response> {\n const auth = await this.getOrCreateAuth();\n // better-auth's HTTP entrypoint (`createBetterAuth.handler`) wraps execution\n // in `runWithAdapter` but NOT `runWithRequestState`. Endpoints that read\n // request-state via `defineRequestState()` (e.g. `should-session-refresh`,\n // `oauth`) therefore throw \"No request state found\" when reached via HTTP.\n // The `customSession` plugin triggers this by invoking the inner\n // `getSession()` endpoint directly, bypassing `to-auth-endpoints`'\n // auto-wrap. We establish the ALS store here so all downstream endpoint\n // calls inherit a valid request-state WeakMap.\n const { runWithRequestState } = await import('@better-auth/core/context');\n const response = await runWithRequestState(new WeakMap(), () => auth.handler(request));\n\n if (response.status >= 500) {\n try {\n const body = await response.clone().text();\n console.error('[AuthManager] better-auth returned error:', response.status, body);\n } catch {\n console.error('[AuthManager] better-auth returned error:', response.status, '(unable to read body)');\n }\n }\n\n return response;\n }\n\n /**\n * Get the better-auth API for programmatic access\n * Use this for server-side operations (e.g., creating users, checking sessions)\n */\n async getApi(): Promise<Auth<any>['api']> {\n const auth = await this.getOrCreateAuth();\n return auth.api;\n }\n\n /**\n * Get the underlying better-auth context for low-level operations such as\n * `internalAdapter.createAccount` / `password.hash`.\n *\n * Used by routes that need to write to better-auth's tables outside the\n * normal endpoint surface — currently only `set-initial-password`, which\n * provisions a credential account for SSO-onboarded users so they can\n * sign in with email/password going forward.\n */\n async getAuthContext(): Promise<any> {\n const auth = await this.getOrCreateAuth();\n return (auth as any).$context;\n }\n\n // ---------------------------------------------------------------------------\n // Device Flow (CLI browser-based login)\n //\n // The device authorization flow (RFC 8628) is now handled entirely by\n // better-auth's `device-authorization` plugin. Endpoints are exposed at\n // `${basePath}/device/{code,token,approve,deny}` and persisted in\n // `sys_device_code`. Enable via `plugins.deviceAuthorization: true` in\n // AuthPluginConfig.\n // ---------------------------------------------------------------------------\n\n getPublicConfig() {\n // Extract social providers info (without sensitive data)\n const socialProviders = [];\n if (this.config.socialProviders) {\n for (const [id, providerConfig] of Object.entries(this.config.socialProviders)) {\n if (providerConfig.enabled !== false) {\n // Map provider ID to friendly name\n const nameMap: Record<string, string> = {\n google: 'Google',\n github: 'GitHub',\n microsoft: 'Microsoft',\n apple: 'Apple',\n facebook: 'Facebook',\n twitter: 'Twitter',\n discord: 'Discord',\n gitlab: 'GitLab',\n linkedin: 'LinkedIn',\n };\n\n socialProviders.push({\n id,\n name: nameMap[id] || id.charAt(0).toUpperCase() + id.slice(1),\n enabled: true,\n type: 'social' as const,\n });\n }\n }\n }\n\n // Append OIDC providers\n if (this.config.oidcProviders?.length) {\n for (const p of this.config.oidcProviders) {\n socialProviders.push({\n id: p.providerId,\n name: p.name ?? (p.providerId.charAt(0).toUpperCase() + p.providerId.slice(1)),\n enabled: true,\n type: 'oidc' as const,\n });\n }\n }\n\n // Extract email/password config (safe fields only)\n const emailPasswordConfig: Partial<EmailAndPasswordConfig> = this.config.emailAndPassword ?? {};\n const emailPassword = {\n enabled: emailPasswordConfig.enabled !== false, // Default to true\n disableSignUp: emailPasswordConfig.disableSignUp ?? false,\n requireEmailVerification: emailPasswordConfig.requireEmailVerification ?? false,\n };\n\n // Extract enabled features\n const pluginConfig: Partial<AuthPluginConfig> = this.config.plugins ?? {};\n const multiOrgEnabled = String(\n (globalThis as any)?.process?.env?.OS_MULTI_ORG_ENABLED ?? 'true',\n ).toLowerCase() !== 'false';\n const features = {\n twoFactor: pluginConfig.twoFactor ?? false,\n passkeys: pluginConfig.passkeys ?? false,\n magicLink: pluginConfig.magicLink ?? false,\n organization: pluginConfig.organization ?? true,\n multiOrgEnabled,\n oidcProvider: pluginConfig.oidcProvider ?? false,\n deviceAuthorization: pluginConfig.deviceAuthorization ?? false,\n };\n\n return {\n emailPassword,\n socialProviders,\n features,\n };\n }\n\n /**\n * Returns the data engine wired into this auth manager. Used by route\n * handlers (e.g. bootstrap-status) that need to query identity tables\n * directly without going through better-auth.\n */\n public getDataEngine(): IDataEngine | undefined {\n return this.config.dataEngine;\n }\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * Canonical plugin-auth manifest source.\n *\n * Both `objectstack.config.ts` (compile-time) and `auth-plugin.ts`\n * (runtime `manifest.register`) import from this file so the two\n * registration paths cannot drift (D7).\n */\n\nimport {\n SysAccount,\n SysApiKey,\n SysDeviceCode,\n SysInvitation,\n SysMember,\n SysJwks,\n SysOauthAccessToken,\n SysOauthApplication,\n SysOauthConsent,\n SysOauthRefreshToken,\n SysOrganization,\n SysSession,\n SysTeam,\n SysTeamMember,\n SysTwoFactor,\n SysUser,\n SysUserPreference,\n SysVerification,\n} from '@objectstack/platform-objects/identity';\n\nexport const AUTH_PLUGIN_ID = 'com.objectstack.plugin-auth';\nexport const AUTH_PLUGIN_VERSION = '3.0.1';\n\n/** Identity objects owned by plugin-auth. */\nexport const authIdentityObjects: any[] = [\n SysUser,\n SysSession,\n SysAccount,\n SysVerification,\n SysOrganization,\n SysMember,\n SysInvitation,\n SysTeam,\n SysTeamMember,\n SysApiKey,\n SysTwoFactor,\n SysUserPreference,\n SysOauthApplication,\n SysOauthAccessToken,\n SysOauthRefreshToken,\n SysOauthConsent,\n SysJwks,\n SysDeviceCode,\n];\n\n/** Manifest header shared by compile-time config and runtime registration. */\nexport const authPluginManifestHeader = {\n id: AUTH_PLUGIN_ID,\n namespace: 'sys',\n version: AUTH_PLUGIN_VERSION,\n type: 'plugin' as const,\n scope: 'system' as const,\n defaultDatasource: 'cloud',\n name: 'Authentication & Identity Plugin',\n description: 'Core authentication objects for ObjectStack (User, Session, Account, Verification)',\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACKA,kBAIO;;;ACNP,sBAAqC;AAErC,oBAAiC;AAQ1B,IAAM,yBAAiD;AAAA,EAC5D,MAAM,+BAAiB;AAAA,EACvB,SAAS,+BAAiB;AAAA,EAC1B,SAAS,+BAAiB;AAAA,EAC1B,cAAc,+BAAiB;AACjC;AAMO,SAAS,oBAAoB,OAAuB;AACzD,SAAO,uBAAuB,KAAK,KAAK;AAC1C;AAsBA,IAAM,kCAA4D;AAAA,EAChE,MAAM,CAAC,cAAc,YAAY;AAAA,EACjC,SAAS,CAAC,cAAc,cAAc,YAAY;AAAA,EAClD,SAAS;AAAA,IACP;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA,EACA,cAAc,CAAC,cAAc,cAAc,YAAY;AACzD;AAEA,IAAM,oBAAoB;AAM1B,SAAS,oBAAoB,OAAyB;AACpD,MAAI,OAAO,UAAU,SAAU,QAAO;AACtC,MAAI,CAAC,kBAAkB,KAAK,KAAK,EAAG,QAAO;AAC3C,QAAM,IAAI,WAAW,KAAK;AAC1B,MAAI,CAAC,OAAO,SAAS,CAAC,EAAG,QAAO;AAEhC,MAAI,KAAK,IAAI,CAAC,IAAI,KAAM,QAAO;AAC/B,QAAM,IAAI,IAAI,KAAK,CAAC;AACpB,MAAI,OAAO,MAAM,EAAE,QAAQ,CAAC,EAAG,QAAO;AACtC,SAAO,EAAE,YAAY;AACvB;AAMA,SAAS,qBACP,OACA,QACG;AACH,MAAI,CAAC,OAAQ,QAAO;AACpB,QAAM,OAAO,gCAAgC,KAAK;AAClD,MAAI,CAAC,KAAM,QAAO;AAClB,aAAW,OAAO,MAAM;AACtB,QAAI,OAAO,QAAQ;AACjB,MAAC,OAAmC,GAAG,IAAI;AAAA,QACxC,OAAmC,GAAG;AAAA,MACzC;AAAA,IACF;AAAA,EACF;AACA,SAAO;AACT;AAQA,SAAS,aAAa,OAA4C;AAChE,QAAM,SAA8B,CAAC;AAErC,aAAW,aAAa,OAAO;AAC7B,UAAM,YAAY,UAAU;AAE5B,QAAI,UAAU,aAAa,MAAM;AAC/B,aAAO,SAAS,IAAI,UAAU;AAAA,IAChC,WAAW,UAAU,aAAa,MAAM;AACtC,aAAO,SAAS,IAAI,EAAE,KAAK,UAAU,MAAM;AAAA,IAC7C,WAAW,UAAU,aAAa,MAAM;AACtC,aAAO,SAAS,IAAI,EAAE,KAAK,UAAU,MAAM;AAAA,IAC7C,WAAW,UAAU,aAAa,MAAM;AACtC,aAAO,SAAS,IAAI,EAAE,KAAK,UAAU,MAAM;AAAA,IAC7C,WAAW,UAAU,aAAa,OAAO;AACvC,aAAO,SAAS,IAAI,EAAE,MAAM,UAAU,MAAM;AAAA,IAC9C,WAAW,UAAU,aAAa,MAAM;AACtC,aAAO,SAAS,IAAI,EAAE,KAAK,UAAU,MAAM;AAAA,IAC7C,WAAW,UAAU,aAAa,OAAO;AACvC,aAAO,SAAS,IAAI,EAAE,MAAM,UAAU,MAAM;AAAA,IAC9C,WAAW,UAAU,aAAa,YAAY;AAC5C,aAAO,SAAS,IAAI,EAAE,QAAQ,UAAU,MAAM;AAAA,IAChD;AAAA,EACF;AAEA,SAAO;AACT;AAsBO,SAAS,6BAA6B,YAAyB;AACpE,aAAO,sCAAqB;AAAA,IAC1B,QAAQ;AAAA,MACN,WAAW;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAMX,kBAAkB;AAAA,MAClB,eAAe;AAAA,MACf,cAAc;AAAA,IAChB;AAAA,IACA,SAAS,OAAO;AAAA,MACd,QAAQ,OACN,EAAE,OAAO,MAAM,QAAQ,QAAQ,MAChB;AACf,cAAM,SAAS,MAAM,WAAW,OAAO,OAAO,IAAI;AAClD,eAAO,qBAAqB,OAAO,MAAM;AAAA,MAC3C;AAAA,MAEA,SAAS,OACP,EAAE,OAAO,OAAO,QAAQ,MAAM,MAAM,MACd;AACtB,cAAM,SAAS,aAAa,KAAK;AAEjC,cAAM,SAAS,MAAM,WAAW,QAAQ,OAAO,EAAE,OAAO,QAAQ,QAAQ,OAAO,CAAC;AAEhF,eAAO,SAAU,qBAAqB,OAAO,MAAM,IAAU;AAAA,MAC/D;AAAA,MAEA,UAAU,OACR,EAAE,OAAO,OAAO,OAAO,QAAQ,QAAQ,MAAM,MAAM,MAIlC;AACjB,cAAM,SAAS,QAAQ,aAAa,KAAK,IAAI,CAAC;AAE9C,cAAM,UAAU,SACZ,CAAC,EAAE,OAAO,OAAO,OAAO,OAAO,OAAO,UAA4B,CAAC,IACnE;AAEJ,cAAM,UAAU,MAAM,WAAW,KAAK,OAAO;AAAA,UAC3C,OAAO;AAAA,UACP,OAAO,SAAS;AAAA,UAChB;AAAA,UACA;AAAA,QACF,CAAC;AAED,eAAO,QAAQ,IAAI,CAAC,MAAM,qBAAqB,OAAO,CAAwB,CAAC;AAAA,MACjF;AAAA,MAEA,OAAO,OACL,EAAE,OAAO,MAAM,MACK;AACpB,cAAM,SAAS,QAAQ,aAAa,KAAK,IAAI,CAAC;AAC9C,eAAO,MAAM,WAAW,MAAM,OAAO,EAAE,OAAO,OAAO,CAAC;AAAA,MACxD;AAAA,MAEA,QAAQ,OACN,EAAE,OAAO,OAAO,OAAO,MACD;AACtB,cAAM,SAAS,aAAa,KAAK;AAGjC,cAAM,SAAS,MAAM,WAAW,QAAQ,OAAO,EAAE,OAAO,OAAO,CAAC;AAChE,YAAI,CAAC,OAAQ,QAAO;AAEpB,cAAM,SAAS,MAAM,WAAW,OAAO,OAAO,EAAE,GAAI,QAAgB,IAAI,OAAO,GAAG,CAAC;AACnF,eAAO,SAAU,qBAAqB,OAAO,MAAM,IAAU;AAAA,MAC/D;AAAA,MAEA,YAAY,OACV,EAAE,OAAO,OAAO,OAAO,MACH;AACpB,cAAM,SAAS,aAAa,KAAK;AAGjC,cAAM,UAAU,MAAM,WAAW,KAAK,OAAO,EAAE,OAAO,OAAO,CAAC;AAC9D,mBAAW,UAAU,SAAS;AAC5B,gBAAM,WAAW,OAAO,OAAO,EAAE,GAAG,QAAQ,IAAI,OAAO,GAAG,CAAC;AAAA,QAC7D;AACA,eAAO,QAAQ;AAAA,MACjB;AAAA,MAEA,QAAQ,OACN,EAAE,OAAO,MAAM,MACG;AAClB,cAAM,SAAS,aAAa,KAAK;AAEjC,cAAM,SAAS,MAAM,WAAW,QAAQ,OAAO,EAAE,OAAO,OAAO,CAAC;AAChE,YAAI,CAAC,OAAQ;AAEb,cAAM,WAAW,OAAO,OAAO,EAAE,OAAO,EAAE,IAAI,OAAO,GAAG,EAAE,CAAC;AAAA,MAC7D;AAAA,MAEA,YAAY,OACV,EAAE,OAAO,MAAM,MACK;AACpB,cAAM,SAAS,aAAa,KAAK;AAEjC,cAAM,UAAU,MAAM,WAAW,KAAK,OAAO,EAAE,OAAO,OAAO,CAAC;AAC9D,mBAAW,UAAU,SAAS;AAC5B,gBAAM,WAAW,OAAO,OAAO,EAAE,OAAO,EAAE,IAAI,OAAO,GAAG,EAAE,CAAC;AAAA,QAC7D;AACA,eAAO,QAAQ;AAAA,MACjB;AAAA,IACF;AAAA,EACF,CAAC;AACH;AAoBO,SAAS,sBAAsB,YAAyB;AAC7D,SAAO;AAAA,IACL,QAAQ,OAAsC,EAAE,OAAO,MAAM,QAAQ,QAAQ,MAAiE;AAC5I,YAAM,aAAa,oBAAoB,KAAK;AAC5C,YAAM,SAAS,MAAM,WAAW,OAAO,YAAY,IAAI;AACvD,aAAO;AAAA,IACT;AAAA,IAEA,SAAS,OAAU,EAAE,OAAO,OAAO,QAAQ,MAAM,MAAM,MAAkG;AACvJ,YAAM,aAAa,oBAAoB,KAAK;AAC5C,YAAM,SAAS,aAAa,KAAK;AACjC,YAAM,SAAS,MAAM,WAAW,QAAQ,YAAY,EAAE,OAAO,QAAQ,QAAQ,OAAO,CAAC;AACrF,aAAO,SAAS,SAAc;AAAA,IAChC;AAAA,IAEA,UAAU,OAAU,EAAE,OAAO,OAAO,OAAO,QAAQ,QAAQ,MAAM,MAAM,MAAkK;AACvO,YAAM,aAAa,oBAAoB,KAAK;AAC5C,YAAM,SAAS,QAAQ,aAAa,KAAK,IAAI,CAAC;AAC9C,YAAM,UAAU,SAAS,CAAC,EAAE,OAAO,OAAO,OAAO,OAAO,OAAO,UAA4B,CAAC,IAAI;AAChG,YAAM,UAAU,MAAM,WAAW,KAAK,YAAY,EAAE,OAAO,QAAQ,OAAO,SAAS,KAAK,QAAQ,QAAQ,CAAC;AACzG,aAAO;AAAA,IACT;AAAA,IAEA,OAAO,OAAO,EAAE,OAAO,MAAM,MAAkE;AAC7F,YAAM,aAAa,oBAAoB,KAAK;AAC5C,YAAM,SAAS,QAAQ,aAAa,KAAK,IAAI,CAAC;AAC9C,aAAO,MAAM,WAAW,MAAM,YAAY,EAAE,OAAO,OAAO,CAAC;AAAA,IAC7D;AAAA,IAEA,QAAQ,OAAU,EAAE,OAAO,OAAO,OAAO,MAAgG;AACvI,YAAM,aAAa,oBAAoB,KAAK;AAC5C,YAAM,SAAS,aAAa,KAAK;AACjC,YAAM,SAAS,MAAM,WAAW,QAAQ,YAAY,EAAE,OAAO,OAAO,CAAC;AACrE,UAAI,CAAC,OAAQ,QAAO;AACpB,YAAM,SAAS,MAAM,WAAW,OAAO,YAAY,EAAE,GAAG,QAAQ,IAAI,OAAO,GAAG,CAAC;AAC/E,aAAO,SAAS,SAAc;AAAA,IAChC;AAAA,IAEA,YAAY,OAAO,EAAE,OAAO,OAAO,OAAO,MAA8F;AACtI,YAAM,aAAa,oBAAoB,KAAK;AAC5C,YAAM,SAAS,aAAa,KAAK;AACjC,YAAM,UAAU,MAAM,WAAW,KAAK,YAAY,EAAE,OAAO,OAAO,CAAC;AACnE,iBAAW,UAAU,SAAS;AAC5B,cAAM,WAAW,OAAO,YAAY,EAAE,GAAG,QAAQ,IAAI,OAAO,GAAG,CAAC;AAAA,MAClE;AACA,aAAO,QAAQ;AAAA,IACjB;AAAA,IAEA,QAAQ,OAAO,EAAE,OAAO,MAAM,MAA+D;AAC3F,YAAM,aAAa,oBAAoB,KAAK;AAC5C,YAAM,SAAS,aAAa,KAAK;AACjC,YAAM,SAAS,MAAM,WAAW,QAAQ,YAAY,EAAE,OAAO,OAAO,CAAC;AACrE,UAAI,CAAC,OAAQ;AACb,YAAM,WAAW,OAAO,YAAY,EAAE,OAAO,EAAE,IAAI,OAAO,GAAG,EAAE,CAAC;AAAA,IAClE;AAAA,IAEA,YAAY,OAAO,EAAE,OAAO,MAAM,MAAiE;AACjG,YAAM,aAAa,oBAAoB,KAAK;AAC5C,YAAM,SAAS,aAAa,KAAK;AACjC,YAAM,UAAU,MAAM,WAAW,KAAK,YAAY,EAAE,OAAO,OAAO,CAAC;AACnE,iBAAW,UAAU,SAAS;AAC5B,cAAM,WAAW,OAAO,YAAY,EAAE,OAAO,EAAE,IAAI,OAAO,GAAG,EAAE,CAAC;AAAA,MAClE;AACA,aAAO,QAAQ;AAAA,IACjB;AAAA,EACF;AACF;;;AC3VA,IAAAA,iBAAiC;AAuC1B,IAAM,mBAAmB;AAAA,EAC9B,WAAW,gCAAiB;AAAA;AAAA,EAC5B,QAAQ;AAAA,IACN,eAAe;AAAA,IACf,WAAW;AAAA,IACX,WAAW;AAAA,EACb;AACF;AAkBO,IAAM,sBAAsB;AAAA,EACjC,WAAW,gCAAiB;AAAA;AAAA,EAC5B,QAAQ;AAAA,IACN,QAAQ;AAAA,IACR,WAAW;AAAA,IACX,WAAW;AAAA,IACX,WAAW;AAAA,IACX,WAAW;AAAA,IACX,WAAW;AAAA,EACb;AACF;AAsBO,IAAM,sBAAsB;AAAA,EACjC,WAAW,gCAAiB;AAAA;AAAA,EAC5B,QAAQ;AAAA,IACN,QAAQ;AAAA,IACR,YAAY;AAAA,IACZ,WAAW;AAAA,IACX,aAAa;AAAA,IACb,cAAc;AAAA,IACd,SAAS;AAAA,IACT,sBAAsB;AAAA,IACtB,uBAAuB;AAAA,IACvB,WAAW;AAAA,IACX,WAAW;AAAA,EACb;AACF;AAeO,IAAM,2BAA2B;AAAA,EACtC,WAAW,gCAAiB;AAAA;AAAA,EAC5B,QAAQ;AAAA,IACN,WAAW;AAAA,IACX,WAAW;AAAA,IACX,WAAW;AAAA,EACb;AACF;AAwBO,IAAM,2BAA2B;AAAA,EACtC,WAAW,gCAAiB;AAAA;AAAA,EAC5B,QAAQ;AAAA,IACN,WAAW;AAAA,IACX,WAAW;AAAA,EACb;AACF;AAeO,IAAM,qBAAqB;AAAA,EAChC,WAAW,gCAAiB;AAAA;AAAA,EAC5B,QAAQ;AAAA,IACN,gBAAgB;AAAA,IAChB,QAAQ;AAAA,IACR,WAAW;AAAA,EACb;AACF;AAiBO,IAAM,yBAAyB;AAAA,EACpC,WAAW,gCAAiB;AAAA;AAAA,EAC5B,QAAQ;AAAA,IACN,gBAAgB;AAAA,IAChB,WAAW;AAAA,IACX,WAAW;AAAA,IACX,WAAW;AAAA,IACX,QAAQ;AAAA,EACV;AACF;AAWO,IAAM,0BAA0B;AAAA,EACrC,sBAAsB;AAAA,EACtB,cAAc;AAChB;AAeO,IAAM,mBAAmB;AAAA,EAC9B,WAAW,gCAAiB;AAAA;AAAA,EAC5B,QAAQ;AAAA,IACN,gBAAgB;AAAA,IAChB,WAAW;AAAA,IACX,WAAW;AAAA,EACb;AACF;AAeO,IAAM,0BAA0B;AAAA,EACrC,WAAW,gCAAiB;AAAA;AAAA,EAC5B,QAAQ;AAAA,IACN,QAAQ;AAAA,IACR,QAAQ;AAAA,IACR,WAAW;AAAA,EACb;AACF;AAcO,IAAM,yBAAyB;AAAA,EACpC,WAAW,gCAAiB;AAAA;AAAA,EAC5B,QAAQ;AAAA,IACN,aAAa;AAAA,IACb,QAAQ;AAAA,EACV;AACF;AAKO,IAAM,8BAA8B;AAAA,EACzC,kBAAkB;AACpB;AAkBO,IAAM,yBAAyB;AAAA,EACpC,WAAW;AAAA,EACX,YAAY;AACd;AAMO,IAAM,4BAA4B;AAAA,EACvC,gBAAgB;AAClB;AAkCO,IAAM,2BAA2B;AAAA,EACtC,WAAW,gCAAiB;AAAA;AAAA,EAC5B,QAAQ;AAAA,IACN,UAAU;AAAA,IACV,cAAc;AAAA,IACd,aAAa;AAAA,IACb,kBAAkB;AAAA,IAClB,aAAa;AAAA,IACb,QAAQ;AAAA,IACR,WAAW;AAAA,IACX,WAAW;AAAA,IACX,cAAc;AAAA,IACd,wBAAwB;AAAA,IACxB,yBAAyB;AAAA,IACzB,YAAY;AAAA,IACZ,eAAe;AAAA,IACf,aAAa;AAAA,IACb,YAAY;AAAA,IACZ,iBAAiB;AAAA,IACjB,mBAAmB;AAAA,IACnB,aAAa;AAAA,EACf;AACF;AAMO,IAAM,gCAAgC;AAuBtC,IAAM,iCAAiC;AAAA,EAC5C,WAAW,gCAAiB;AAAA;AAAA,EAC5B,QAAQ;AAAA,IACN,UAAU;AAAA,IACV,WAAW;AAAA,IACX,QAAQ;AAAA,IACR,aAAa;AAAA,IACb,WAAW;AAAA,IACX,WAAW;AAAA,IACX,WAAW;AAAA,EACb;AACF;AAuBO,IAAM,kCAAkC;AAAA,EAC7C,WAAW,gCAAiB;AAAA;AAAA,EAC5B,QAAQ;AAAA,IACN,UAAU;AAAA,IACV,WAAW;AAAA,IACX,QAAQ;AAAA,IACR,aAAa;AAAA,IACb,WAAW;AAAA,IACX,WAAW;AAAA,IACX,UAAU;AAAA,EACZ;AACF;AAqBO,IAAM,4BAA4B;AAAA,EACvC,WAAW,gCAAiB;AAAA;AAAA,EAC5B,QAAQ;AAAA,IACN,UAAU;AAAA,IACV,QAAQ;AAAA,IACR,aAAa;AAAA,IACb,WAAW;AAAA,IACX,WAAW;AAAA,EACb;AACF;AAuBO,IAAM,0BAA0B;AAAA,EACrC,WAAW,gCAAiB;AAAA;AAAA,EAC5B,QAAQ;AAAA,IACN,YAAY;AAAA,IACZ,UAAU;AAAA,IACV,QAAQ;AAAA,IACR,WAAW;AAAA,IACX,cAAc;AAAA,IACd,iBAAiB;AAAA,IACjB,UAAU;AAAA,EACZ;AACF;AAOO,SAAS,6BAA6B;AAC3C,SAAO;AAAA,IACL,WAAW;AAAA,IACX,MAAM;AAAA,MACJ,QAAQ;AAAA,IACV;AAAA,EACF;AACF;AASO,SAAS,yBAAyB;AACvC,SAAO;AAAA,IACL,MAAM;AAAA,MACJ,QAAQ;AAAA,IACV;AAAA,IACA,SAAS;AAAA,MACP,QAAQ;AAAA,IACV;AAAA,EACF;AACF;AAgBO,SAAS,gCAAgC;AAC9C,SAAO;AAAA,IACL,cAAc;AAAA,IACd,QAAQ;AAAA,IACR,YAAY;AAAA,IACZ,MAAM;AAAA,IACN,YAAY;AAAA,IACZ,SAAS;AAAA,MACP,QAAQ;AAAA,IACV;AAAA,EACF;AACF;AAoBO,IAAM,mBAAmB;AAAA,EAC9B,WAAW,gCAAiB;AAAA;AAAA,EAC5B,QAAQ;AAAA,IACN,WAAW;AAAA,IACX,YAAY;AAAA,IACZ,WAAW;AAAA,IACX,WAAW;AAAA,EACb;AACF;AAOO,SAAS,uBAAuB;AACrC,SAAO;AAAA,IACL,MAAM;AAAA,EACR;AACF;AAkBO,SAAS,iCAAiC;AAC/C,SAAO;AAAA,IACL,aAAa;AAAA,IACb,kBAAkB;AAAA,IAClB,mBAAmB;AAAA,IACnB,cAAc;AAAA,EAChB;AACF;AAOO,IAAM,gCAAgC;AAetC,SAAS,uCAAuC;AACrD,SAAO;AAAA,IACL,YAAY;AAAA,EACd;AACF;;;ACxnBA,SAAS,wBAAiC;AACxC,MAAI,OAAO,eAAe,YAAa,QAAO;AAC9C,QAAM,OAAQ,WAAmB;AACjC,SACE,QAAQ,MAAM,UAAU,YAAY,KACpC,QAAQ,MAAM,KAAK,OAAO,WAAW,KAAK,CAAC,KAC3C,QAAQ,MAAM,KAAK,UAAU;AAEjC;AA8BA,IAAM,4CAAN,MAAmD;AAAA,EAAnD;AACE,SAAQ,UAAyB;AAAA;AAAA,EAEjC,IAAO,OAAU,IAAgB;AAC/B,UAAM,OAAO,KAAK;AAClB,SAAK,UAAU;AACf,QAAI;AACF,YAAM,SAAS,GAAG;AAClB,UAAI,UAAU,OAAQ,OAA4B,SAAS,YAAY;AACrE,eAAQ,OAA4B,QAAQ,MAAM;AAChD,eAAK,UAAU;AAAA,QACjB,CAAC;AAAA,MACH;AACA,WAAK,UAAU;AACf,aAAO;AAAA,IACT,SAAS,KAAK;AACZ,WAAK,UAAU;AACf,YAAM;AAAA,IACR;AAAA,EACF;AAAA,EAEA,WAA0B;AACxB,WAAO,KAAK;AAAA,EACd;AACF;AAkBA,SAAS,0CAAgD;AACvD,MAAI,CAAC,sBAAsB,EAAG;AAC9B,QAAM,MAAM,uBAAO,IAAI,oBAAoB;AAC3C,QAAM,IAAI;AACV,MAAI,CAAC,EAAE,GAAG,GAAG;AACX,MAAE,GAAG,IAAI,EAAE,SAAS,kBAAkB,OAAO,GAAG,SAAS,CAAC,EAAE;AAAA,EAC9D;AACA,MAAI,CAAC,EAAE,GAAG,EAAE,QAAS,GAAE,GAAG,EAAE,UAAU,CAAC;AACvC,MAAI,CAAC,EAAE,GAAG,EAAE,QAAQ,0BAA0B;AAC5C,MAAE,GAAG,EAAE,QAAQ,2BAA2B,IAAI,0CAA0C;AAExF,YAAQ;AAAA,MACN;AAAA,IAEF;AAAA,EACF;AACF;AAmGO,IAAM,cAAN,MAAkB;AAAA,EAIvB,YAAY,QAA4B;AAHxC,SAAQ,OAAyB;AAI/B,SAAK,SAAS;AAMd,4CAAwC;AAGxC,QAAI,OAAO,cAAc;AACvB,WAAK,OAAO,OAAO;AAAA,IACrB;AAAA,EAGF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAc,kBAAsC;AAClD,QAAI,CAAC,KAAK,MAAM;AACd,WAAK,OAAO,MAAM,KAAK,mBAAmB;AAAA,IAC5C;AACA,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA,EAKA,MAAc,qBAAyC;AACrD,UAAM,EAAE,WAAW,IAAI,MAAM,OAAO,aAAa;AACjD,UAAM,UAAU,MAAM,KAAK,gBAAgB;AAC3C,UAAM,iBAAiB,MAAM,KAAK,sBAAsB;AACxD,UAAM,mBAAsC;AAAA;AAAA,MAE1C,QAAQ,KAAK,OAAO,UAAU,KAAK,eAAe;AAAA,MAClD,SAAS,KAAK,OAAO,WAAW;AAAA,MAChC,UAAU,KAAK,OAAO,YAAY;AAAA;AAAA,MAGlC,UAAU,KAAK,qBAAqB;AAAA;AAAA;AAAA;AAAA;AAAA,MAMpC,MAAM;AAAA,QACJ,GAAG;AAAA,MACL;AAAA,MACA,SAAS;AAAA,QACP,GAAG;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,QAiBH,gBAAgB;AAAA,UACd,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,UAUT,2BAA2B;AAAA,UAC3B,GAAK,KAAK,QAAgB,SAAS,kBAAkB,CAAC;AAAA,UACtD,kBAAkB,MAAM,KAAK,oBAAI,IAAI;AAAA,YACnC;AAAA,YACA,GAAK,KAAK,QAAgB,SAAS,gBAAgB,oBAAoB,CAAC;AAAA,UAC1E,CAAC,CAAC;AAAA,QACJ;AAAA,MACF;AAAA,MACA,cAAc;AAAA,QACZ,GAAG;AAAA,MACL;AAAA;AAAA,MAGA,GAAI,KAAK,OAAO,kBAAkB,EAAE,iBAAiB,KAAK,OAAO,gBAAuB,IAAI,CAAC;AAAA;AAAA,MAG7F,kBAAkB;AAAA,QAChB,SAAS,KAAK,OAAO,kBAAkB,WAAW;AAAA,QAClD,GAAI,iBAAiB,EAAE,UAAU,eAAe,IAAI,CAAC;AAAA,QACrD,GAAI,KAAK,OAAO,kBAAkB,iBAAiB,OAC/C,EAAE,eAAe,KAAK,OAAO,iBAAiB,cAAc,IAAI,CAAC;AAAA,QACrE,GAAI,KAAK,OAAO,kBAAkB,4BAA4B,OAC1D,EAAE,0BAA0B,KAAK,OAAO,iBAAiB,yBAAyB,IAAI,CAAC;AAAA,QAC3F,GAAI,KAAK,OAAO,kBAAkB,qBAAqB,OACnD,EAAE,mBAAmB,KAAK,OAAO,iBAAiB,kBAAkB,IAAI,CAAC;AAAA,QAC7E,GAAI,KAAK,OAAO,kBAAkB,qBAAqB,OACnD,EAAE,mBAAmB,KAAK,OAAO,iBAAiB,kBAAkB,IAAI,CAAC;AAAA,QAC7E,GAAI,KAAK,OAAO,kBAAkB,+BAA+B,OAC7D,EAAE,6BAA6B,KAAK,OAAO,iBAAiB,4BAA4B,IAAI,CAAC;AAAA,QACjG,GAAI,KAAK,OAAO,kBAAkB,cAAc,OAC5C,EAAE,YAAY,KAAK,OAAO,iBAAiB,WAAW,IAAI,CAAC;AAAA,QAC/D,GAAI,KAAK,OAAO,kBAAkB,iCAAiC,OAC/D,EAAE,+BAA+B,KAAK,OAAO,iBAAiB,8BAA8B,IAAI,CAAC;AAAA,QACrG,mBAAmB,OAAO,EAAE,MAAM,KAAK,MAAM,MAA0F;AACrI,gBAAM,QAAQ,KAAK,gBAAgB;AACnC,cAAI,CAAC,OAAO;AACV,oBAAQ;AAAA,cACN,8CAA8C,KAAK,KAAK,wCAAwC,GAAG;AAAA,YACrG;AACA;AAAA,UACF;AACA,gBAAM,SAAS,KAAK,OAAO,kBAAkB,+BAA+B,KAAK;AACjF,cAAI;AACF,kBAAM,MAAM,aAAa;AAAA,cACvB,UAAU;AAAA,cACV,IAAI,EAAE,SAAS,KAAK,OAAO,GAAI,KAAK,OAAO,EAAE,MAAM,KAAK,KAAK,IAAI,CAAC,EAAG;AAAA,cACrE,MAAM;AAAA,gBACJ,MAAM,EAAE,MAAM,KAAK,QAAQ,KAAK,OAAO,OAAO,KAAK,OAAO,IAAI,KAAK,GAAG;AAAA,gBACtE,UAAU;AAAA,gBACV;AAAA,gBACA,kBAAkB,KAAK,MAAM,SAAS,EAAE;AAAA,gBACxC,SAAS,KAAK,WAAW;AAAA,cAC3B;AAAA,cACA,eAAe;AAAA,cACf,WAAW,KAAK;AAAA,YAClB,CAAC;AAAA,UACH,SAAS,KAAU;AACjB,oBAAQ,MAAM,2CAA2C,KAAK,WAAW,GAAG,EAAE;AAC9E,kBAAM;AAAA,UACR;AAAA,QACF;AAAA,MACF;AAAA;AAAA,MAGA,GAAI,KAAK,OAAO,qBAAqB,KAAK,OAAO,eAAe;AAAA,QAC9D,mBAAmB;AAAA,UACjB,GAAI,KAAK,OAAO,mBAAmB,gBAAgB,OAC/C,EAAE,cAAc,KAAK,OAAO,kBAAkB,aAAa,IAAI,CAAC;AAAA,UACpE,GAAI,KAAK,OAAO,mBAAmB,gBAAgB,OAC/C,EAAE,cAAc,KAAK,OAAO,kBAAkB,aAAa,IAAI,CAAC;AAAA,UACpE,GAAI,KAAK,OAAO,mBAAmB,+BAA+B,OAC9D,EAAE,6BAA6B,KAAK,OAAO,kBAAkB,4BAA4B,IAAI,CAAC;AAAA,UAClG,GAAI,KAAK,OAAO,mBAAmB,aAAa,OAC5C,EAAE,WAAW,KAAK,OAAO,kBAAkB,UAAU,IAAI,CAAC;AAAA,UAC9D,uBAAuB,OAAO,EAAE,MAAM,KAAK,MAAM,MAA0F;AACzI,kBAAM,QAAQ,KAAK,gBAAgB;AACnC,gBAAI,CAAC,OAAO;AACV,sBAAQ;AAAA,gBACN,kDAAkD,KAAK,KAAK,wCAAwC,GAAG;AAAA,cACzG;AACA;AAAA,YACF;AACA,kBAAM,SAAS,KAAK,OAAO,mBAAmB,aAAa,KAAK;AAChE,gBAAI;AACF,oBAAM,MAAM,aAAa;AAAA,gBACvB,UAAU;AAAA,gBACV,IAAI,EAAE,SAAS,KAAK,OAAO,GAAI,KAAK,OAAO,EAAE,MAAM,KAAK,KAAK,IAAI,CAAC,EAAG;AAAA,gBACrE,MAAM;AAAA,kBACJ,MAAM,EAAE,MAAM,KAAK,QAAQ,KAAK,OAAO,OAAO,KAAK,OAAO,IAAI,KAAK,GAAG;AAAA,kBACtE,iBAAiB;AAAA,kBACjB;AAAA,kBACA,kBAAkB,KAAK,MAAM,SAAS,EAAE;AAAA,kBACxC,SAAS,KAAK,WAAW;AAAA,gBAC3B;AAAA,gBACA,eAAe;AAAA,gBACf,WAAW,KAAK;AAAA,cAClB,CAAC;AAAA,YACH,SAAS,KAAU;AACjB,sBAAQ,MAAM,+CAA+C,KAAK,WAAW,GAAG,EAAE;AAClF,oBAAM;AAAA,YACR;AAAA,UACF;AAAA,QACF;AAAA,MACF,IAAI,CAAC;AAAA;AAAA,MAGL,SAAS;AAAA,QACP,GAAG;AAAA,QACH,WAAW,KAAK,OAAO,SAAS,aAAa,KAAK,KAAK,KAAK;AAAA;AAAA,QAC5D,WAAW,KAAK,OAAO,SAAS,aAAa,KAAK,KAAK;AAAA;AAAA,MACzD;AAAA;AAAA,MAGA;AAAA;AAAA;AAAA;AAAA,MAKA,GAAI,KAAK,OAAO,gBAAgB,EAAE,eAAe,KAAK,OAAO,cAAc,IAAI,CAAC;AAAA;AAAA;AAAA,MAIhF,IAAI,MAAM;AACR,cAAM,UAAoB,CAAC,GAAI,KAAK,OAAO,kBAAkB,CAAC,CAAE;AAEhE,cAAM,aAAa,QAAQ,IAAI;AAC/B,YAAI,cAAc,eAAe,KAAK;AACpC,qBAAW,MAAM,GAAG,EAAE,IAAI,OAAK,EAAE,KAAK,CAAC,EAAE,OAAO,OAAO,EAAE,QAAQ,OAAK;AACpE,gBAAI,CAAC,QAAQ,SAAS,CAAC,EAAG,SAAQ,KAAK,CAAC;AAAA,UAC1C,CAAC;AAAA,QACH;AAMA,YAAI,CAAC,QAAQ,WAAW,CAAC,cAAc,eAAe,MAAM;AAC1D,kBAAQ,KAAK,oBAAoB;AACjC,kBAAQ,KAAK,sBAAsB;AACnC,kBAAQ,KAAK,uBAAuB;AAAA,QACtC;AACA,eAAO,QAAQ,SAAS,EAAE,gBAAgB,QAAQ,IAAI,CAAC;AAAA,MACzD,GAAG;AAAA;AAAA,MAGH,GAAI,KAAK,OAAO,WAAW;AAAA,QACzB,UAAU;AAAA,UACR,GAAI,KAAK,OAAO,SAAS,wBACrB,EAAE,uBAAuB,KAAK,OAAO,SAAS,sBAAsB,IAAI,CAAC;AAAA,UAC7E,GAAI,KAAK,OAAO,SAAS,oBAAoB,OACzC,EAAE,kBAAkB,KAAK,OAAO,SAAS,iBAAiB,IAAI,CAAC;AAAA,UACnE,GAAI,KAAK,OAAO,SAAS,oBAAoB,OACzC,EAAE,kBAAkB,KAAK,OAAO,SAAS,iBAAiB,IAAI,CAAC;AAAA,UACnE,GAAI,KAAK,OAAO,SAAS,gBAAgB,OACrC,EAAE,cAAc,KAAK,OAAO,SAAS,aAAa,IAAI,CAAC;AAAA,QAC7D;AAAA,MACF,IAAI,CAAC;AAAA,IACP;AAEA,WAAO,WAAW,gBAAgB;AAAA,EACpC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAqBA,MAAc,wBAEZ;AACA,QAAI,CAAC,sBAAsB,EAAG,QAAO;AACrC,QAAI;AACF,YAAM,EAAE,YAAY,IAAI,MAAM,OAAO,yBAAyB;AAC9D,YAAM,SAAS,EAAE,GAAG,OAAO,GAAG,IAAI,GAAG,GAAG,OAAO,IAAI,QAAQ,MAAM,QAAQ,KAAK,EAAE;AAChF,YAAM,QAAQ,CAAC,MAA0B;AACvC,YAAI,IAAI;AACR,iBAAS,IAAI,GAAG,IAAI,EAAE,QAAQ,IAAK,MAAK,EAAE,CAAC,EAAG,SAAS,EAAE,EAAE,SAAS,GAAG,GAAG;AAC1E,eAAO;AAAA,MACT;AACA,YAAM,cAAc,CAAC,UAAkB,YACrC,YAAY,SAAS,UAAU,MAAM,GAAG,SAAS,MAAM;AACzD,aAAO;AAAA,QACL,MAAM,OAAO,aAAqB;AAChC,gBAAM,YAAa,WAAmB,OAAO,gBAAgB,IAAI,WAAW,EAAE,CAAC;AAC/E,gBAAM,UAAU,MAAM,SAAS;AAC/B,gBAAM,MAAM,MAAM,YAAY,UAAU,OAAO;AAC/C,iBAAO,GAAG,OAAO,IAAI,MAAM,GAAG,CAAC;AAAA,QACjC;AAAA,QACA,QAAQ,OAAO,EAAE,MAAM,SAAS,MAAM;AACpC,gBAAM,CAAC,SAAS,MAAM,IAAI,KAAK,MAAM,GAAG;AACxC,cAAI,CAAC,WAAW,CAAC,OAAQ,OAAM,IAAI,MAAM,uBAAuB;AAChE,gBAAM,SAAS,MAAM,YAAY,UAAU,OAAO;AAClD,iBAAO,MAAM,MAAM,MAAM;AAAA,QAC3B;AAAA,MACF;AAAA,IACF,SAAS,KAAU;AACjB,cAAQ;AAAA,QACN,uEAAuE,KAAK,WAAW,GAAG;AAAA,MAC5F;AACA,aAAO;AAAA,IACT;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAc,kBAAkC;AAC9C,UAAM,eAA0C,KAAK,OAAO,WAAW,CAAC;AACxE,UAAM,UAAiB,CAAC;AAOxB,UAAM,UAAU;AAAA,MACd,cAAc,aAAa,gBAAgB;AAAA,MAC3C,WAAW,aAAa,aAAa;AAAA,MACrC,UAAU,aAAa,YAAY;AAAA,MACnC,WAAW,aAAa,aAAa;AAAA,MACrC,cAAc,aAAa,gBAAgB;AAAA,MAC3C,qBAAqB,aAAa,uBAAuB;AAAA,MACzD,OAAO,aAAa,SAAS;AAAA,IAC/B;AAgBA,UAAM,EAAE,OAAO,IAAI,MAAM,OAAO,4BAA4B;AAC5D,YAAQ,KAAK,OAAO,CAAC;AAErB,QAAI,QAAQ,cAAc;AACxB,YAAM,EAAE,aAAa,IAAI,MAAM,OAAO,kCAAkC;AASxE,UAAI;AACJ,YAAM,QAAQ,KAAK,OAAO;AAC1B,UAAI,SAAS,MAAM,SAAS,GAAG;AAC7B,YAAI;AACF,gBAAM,YAAY,MAAM,OAAO,yCAAyC;AACxE,gBAAM,EAAE,WAAW,UAAU,cAAc,qBAAqB,IAAI;AAMpE,gBAAM,eAAe,wBAAwB;AAC7C,cAAI,aAAa,YAAY,OAAO,SAAS,eAAe,UAAU;AACpE,kBAAM,QAA6B,eAAe,EAAE,GAAG,aAAa,IAAI,CAAC;AACzE,kBAAM,QAAQ,SAAS;AACvB,uBAAW,QAAQ,OAAO;AACxB,kBAAI,CAAC,KAAM;AACX,kBAAI,MAAM,IAAI,EAAG;AACjB,oBAAM,IAAI,IAAI,UAAU,QAAQ,KAAK;AAAA,YACvC;AACA,6BAAiB;AAAA,UACnB;AAAA,QACF,QAAQ;AACN,2BAAiB;AAAA,QACnB;AAAA,MACF;AACA,cAAQ,KAAK,aAAa;AAAA,QACxB,QAAQ,8BAA8B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,QAQtC,OAAO,EAAE,SAAS,KAAK;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,QAMvB,sCAAsC;AAAA,QACtC,GAAI,iBAAiB,EAAE,OAAO,eAAe,IAAI,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,QAalD,mBAAmB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,UAMjB,0BAA0B,YAAY;AACpC,kBAAM,OAAO;AAAA,cACV,YAAoB,SAAS,KAAK,wBAAwB;AAAA,YAC7D,EAAE,YAAY;AACd,gBAAI,SAAS,SAAS;AACpB,oBAAM,EAAE,SAAS,IAAI,MAAM,OAAO,iBAAiB;AACnD,oBAAM,IAAI,SAAS,aAAa;AAAA,gBAC9B,SACE;AAAA,cACJ,CAAC;AAAA,YACH;AAAA,UACF;AAAA,UACA,0BAA0B,OAAO,EAAE,cAAAC,eAAc,OAAO,MAAW;AACjE,kBAAM,UAAUA,eAAc;AAC9B,kBAAM,QAAQ,QAAQ;AACtB,gBAAI,CAAC,WAAW,CAAC,MAAO;AAExB,kBAAMC,cAAa,KAAK,OAAO;AAC/B,gBAAI,CAACA,YAAY;AAEjB,gBAAI;AACJ,gBAAI;AACF,oBAAM,UAAU,MAAMA,YAAW,QAAQ,oBAAoB;AAAA,gBAC3D,OAAO,EAAE,IAAI,MAAM;AAAA,cACrB,CAAC;AACD,4BAAc,SAAS;AAAA,YACzB,QAAQ;AACN;AAAA,YACF;AACA,gBAAI,CAAC,eAAe,gBAAgB,QAAS;AAE7C,gBAAI,aAAa;AACjB,gBAAI;AACF,oBAAM,OAAO,MAAMA,YAAW,KAAK,mBAAmB;AAAA,gBACpD,OAAO,EAAE,iBAAiB,MAAM;AAAA,cAClC,CAAC;AACD,4BAAc,QAAQ,CAAC,GAAG;AAAA,gBACxB,CAAC,MAAW,GAAG,WAAW,cAAc,GAAG,WAAW;AAAA,cACxD,EAAE;AAAA,YACJ,QAAQ;AACN;AAAA,YACF;AAEA,gBAAI,aAAa,GAAG;AAClB,oBAAM,EAAE,SAAS,IAAI,MAAM,OAAO,iBAAiB;AACnD,oBAAM,IAAI,SAAS,aAAa;AAAA,gBAC9B,SACE,yCAAyC,UAAU;AAAA,cAGvD,CAAC;AAAA,YACH;AAAA,UACF;AAAA,QACF;AAAA;AAAA;AAAA;AAAA,QAIA,qBAAqB,OAAO,EAAE,OAAO,gBAAgB,YAAY,cAAc,KAAK,QAAQ,MAAM;AAChG,gBAAM,WAAW,KAAK,OAAO,WAAW,IAAI,QAAQ,OAAO,EAAE;AAC7D,gBAAM,YAAY,GAAG,OAAO,sBAAsB,WAAW,EAAE;AAC/D,gBAAM,eAAe,KAAK,gBAAgB;AAC1C,cAAI,CAAC,cAAc;AACjB,oBAAQ;AAAA,cACN,sDACO,cAAc,UAAU,KAAK,QAAQ,WAAW,cAAc,WAC5D,WAAW,IAAI,cAAc,SAAS,MAAM,SAAS,SAAS,UAC/D,SAAS;AAAA,YACnB;AACA;AAAA,UACF;AACA,cAAI;AACF,kBAAM,aAAa,aAAa;AAAA,cAC9B,UAAU;AAAA,cACV,IAAI;AAAA,cACJ,MAAM;AAAA,gBACJ,SAAS;AAAA,kBACP,MAAM,SAAS,MAAM,QAAQ,SAAS,MAAM,SAAS;AAAA,kBACrD,OAAO,SAAS,MAAM,SAAS;AAAA,gBACjC;AAAA,gBACA,cAAc,EAAE,MAAM,KAAK,QAAQ,WAAW,eAAe;AAAA,gBAC7D,MAAM,WAAW,QAAQ;AAAA,gBACzB;AAAA,gBACA,SAAS,KAAK,WAAW;AAAA,cAC3B;AAAA,cACA,eAAe;AAAA,cACf,WAAW,WAAW;AAAA,YACxB,CAAC;AAAA,UACH,SAAS,KAAU;AACjB,oBAAQ,MAAM,6CAA6C,KAAK,WAAW,GAAG,EAAE;AAChF,kBAAM;AAAA,UACR;AAAA,QACF;AAAA,MACF,CAAC,CAAC;AAAA,IACJ;AAEA,QAAI,QAAQ,WAAW;AACrB,YAAM,EAAE,UAAU,IAAI,MAAM,OAAO,gCAAgC;AACnE,cAAQ,KAAK,UAAU;AAAA,QACrB,QAAQ,2BAA2B;AAAA,MACrC,CAAC,CAAC;AAAA,IACJ;AAEA,QAAI,QAAQ,OAAO;AACjB,YAAM,EAAE,MAAM,IAAI,MAAM,OAAO,2BAA2B;AAM1D,cAAQ,KAAK,MAAM;AAAA,QACjB,QAAQ,uBAAuB;AAAA,MACjC,CAAC,CAAC;AAAA,IACJ;AAEA,QAAI,QAAQ,WAAW;AACrB,YAAM,EAAE,UAAU,IAAI,MAAM,OAAO,gCAAgC;AAEnE,cAAQ,KAAK,UAAU;AAAA,QACrB,eAAe,OAAO,EAAE,OAAO,gBAAgB,KAAK,MAAM,MAAM;AAC9D,gBAAM,eAAe,KAAK,gBAAgB;AAC1C,cAAI,CAAC,cAAc;AACjB,oBAAQ;AAAA,cACN,0CAA0C,cAAc,wCAAwC,GAAG;AAAA,YACrG;AACA;AAAA,UACF;AACA,cAAI;AACF,kBAAM,aAAa,aAAa;AAAA,cAC9B,UAAU;AAAA,cACV,IAAI;AAAA,cACJ,MAAM;AAAA,gBACJ,cAAc;AAAA,gBACd;AAAA,gBACA,kBAAkB;AAAA,gBAClB,SAAS,KAAK,WAAW;AAAA,cAC3B;AAAA,YACF,CAAC;AAAA,UACH,SAAS,KAAU;AACjB,oBAAQ,MAAM,uCAAuC,KAAK,WAAW,GAAG,EAAE;AAC1E,kBAAM;AAAA,UACR;AAAA,QACF;AAAA,MACF,CAAC,CAAC;AAAA,IACJ;AAGA,QAAI,KAAK,OAAO,eAAe,QAAQ;AACrC,YAAM,EAAE,aAAa,IAAI,MAAM,OAAO,mCAAmC;AACzE,cAAQ,KAAK,aAAa;AAAA,QACxB,QAAQ,KAAK,OAAO,cAAc,IAAI,QAAM;AAAA,UAC1C,YAAY,EAAE;AAAA,UACd,GAAI,EAAE,eAAe,EAAE,cAAc,EAAE,aAAa,IAAI,CAAC;AAAA,UACzD,GAAI,EAAE,SAAS,EAAE,QAAQ,EAAE,OAAO,IAAI,CAAC;AAAA,UACvC,GAAI,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,iBAAiB,IAAI,CAAC;AAAA,UACrE,GAAI,EAAE,WAAW,EAAE,UAAU,EAAE,SAAS,IAAI,CAAC;AAAA,UAC7C,GAAI,EAAE,cAAc,EAAE,aAAa,EAAE,YAAY,IAAI,CAAC;AAAA,UACtD,UAAU,EAAE;AAAA,UACZ,cAAc,EAAE;AAAA,UAChB,GAAI,EAAE,SAAS,EAAE,QAAQ,EAAE,OAAO,IAAI,CAAC;AAAA,UACvC,GAAI,EAAE,QAAQ,OAAO,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;AAAA,QAC3C,EAAE;AAAA,MACJ,CAAC,CAAC;AAAA,IACJ;AAYA,QAAI,QAAQ,cAAc;AAKxB,YAAM,EAAE,IAAI,IAAI,MAAM,OAAO,qBAAqB;AAClD,cAAQ,KAAK,IAAI,EAAE,QAAQ,qBAAqB,EAAE,CAAC,CAAC;AAEpD,YAAM,EAAE,cAAc,IAAI,MAAM,OAAO,6BAA6B;AACpE,YAAM,WAAW,KAAK,OAAO,WAAW,IAAI,QAAQ,OAAO,EAAE;AAC7D,cAAQ,KAAK,cAAc;AAAA;AAAA,QAEzB,WAAW,GAAG,OAAO;AAAA,QACrB,aAAa,GAAG,OAAO;AAAA,QACvB,QAAQ,+BAA+B;AAAA,MACzC,CAAC,CAAC;AAAA,IACJ;AAUA,QAAI,QAAQ,qBAAqB;AAC/B,YAAM,EAAE,oBAAoB,IAAI,MAAM,OAAO,0CAA0C;AACvF,YAAM,WAAW,KAAK,OAAO,WAAW,IAAI,QAAQ,OAAO,EAAE;AAC7D,cAAQ,KAAK,oBAAoB;AAAA,QAC/B,iBAAiB,GAAG,OAAO;AAAA,QAC3B,QAAQ,qCAAqC;AAAA,MAC/C,CAAC,CAAC;AAAA,IACJ;AAqBA,UAAM,aAAa,KAAK,OAAO;AAC/B,QAAI,YAAY;AACd,YAAM,EAAE,cAAc,IAAI,MAAM,OAAO,oCAAoC;AAC3E,cAAQ,KAAK,cAAc,OAAO,EAAE,MAAM,QAAQ,MAAM;AACtD,YAAI,CAAC,MAAM,GAAI,QAAO,EAAE,MAAM,QAAQ;AAEtC,cAAM,kBAAkB,YAA8B;AACpD,cAAI;AACF,kBAAM,QAAQ,MAAM,WAAW,KAAK,2BAA2B;AAAA,cAC7D,OAAO,EAAE,SAAS,KAAK,GAAG;AAAA,cAC1B,OAAO;AAAA,YACT,CAAC;AACD,kBAAM,iBAAiB,MAAM,QAAQ,KAAK,IAAI,QAAQ,CAAC,GAAG;AAAA,cACxD,CAAC,MAAW,CAAC,EAAE;AAAA,YACjB;AACA,gBAAI,cAAc,WAAW,EAAG,QAAO;AACvC,kBAAM,OAAO,MAAM,WAAW,KAAK,sBAAsB,EAAE,OAAO,GAAG,CAAC;AACtE,kBAAM,YAAY,MAAM,QAAQ,IAAI,IAAI,OAAO,CAAC,GAAG;AAAA,cACjD,CAAC,MAAW,EAAE,SAAS;AAAA,YACzB;AACA,gBAAI,CAAC,SAAU,QAAO;AACtB,mBAAO,cAAc;AAAA,cACnB,CAAC,MAAW,EAAE,sBAAsB,SAAS;AAAA,YAC/C;AAAA,UACF,QAAQ;AACN,mBAAO;AAAA,UACT;AAAA,QACF;AAEA,cAAM,mBAAmB,YAA8B;AACrD,cAAI;AACF,kBAAM,QAAS,SAAiB;AAChC,gBAAI,CAAC,MAAO,QAAO;AACnB,kBAAM,UAAU,MAAM,WAAW,KAAK,cAAc;AAAA,cAClD,OAAO,EAAE,SAAS,KAAK,IAAI,iBAAiB,MAAM;AAAA,cAClD,OAAO;AAAA,YACT,CAAC;AACD,oBAAQ,MAAM,QAAQ,OAAO,IAAI,UAAU,CAAC,GAAG,KAAK,CAAC,MAAW;AAI9D,oBAAM,MAAM,OAAO,GAAG,SAAS,WAAW,EAAE,OAAO;AACnD,oBAAM,QAAQ,IAAI,MAAM,GAAG,EAAE,IAAI,CAAC,MAAc,EAAE,KAAK,EAAE,YAAY,CAAC;AACtE,qBAAO,MAAM,SAAS,OAAO,KAAK,MAAM,SAAS,OAAO;AAAA,YAC1D,CAAC;AAAA,UACH,QAAQ;AACN,mBAAO;AAAA,UACT;AAAA,QACF;AAEA,cAAM,UAAW,MAAM,gBAAgB,KAAO,MAAM,iBAAiB;AACrE,YAAI,CAAC,QAAS,QAAO,EAAE,MAAM,QAAQ;AACrC,eAAO,EAAE,MAAM,EAAE,GAAG,MAAM,MAAM,QAAQ,GAAG,QAAQ;AAAA,MACrD,CAAC,CAAC;AAAA,IACJ;AAEA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAcQ,uBAA4B;AAElC,QAAI,KAAK,OAAO,YAAY;AAM1B,aAAO,6BAA6B,KAAK,OAAO,UAAU;AAAA,IAC5D;AAGA,YAAQ;AAAA,MACN;AAAA,IAGF;AAIA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKQ,iBAAyB;AAC/B,UAAM,YAAY,QAAQ,IAAI;AAE9B,QAAI,CAAC,WAAW;AAGd,YAAM,iBAAiB,gBAAgB,KAAK,IAAI;AAEhD,cAAQ;AAAA,QACN;AAAA,MAIF;AAEA,aAAO;AAAA,IACT;AAEA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAYA,kBAAkB,KAAmB;AACnC,QAAI,KAAK,MAAM;AACb,cAAQ;AAAA,QACN;AAAA,MAEF;AACA;AAAA,IACF;AACA,SAAK,SAAS,EAAE,GAAG,KAAK,QAAQ,SAAS,IAAI;AAAA,EAC/C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWA,gBAAgB,OAAwC;AACtD,SAAK,OAAO,eAAe;AAAA,EAC7B;AAAA;AAAA,EAGQ,kBAA6C;AACnD,WAAO,KAAK,OAAO;AAAA,EACrB;AAAA;AAAA,EAGQ,aAAqB;AAC3B,WAAO,KAAK,OAAO,WAAW;AAAA,EAChC;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,kBAAsC;AACpC,WAAO,KAAK,gBAAgB;AAAA,EAC9B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAaA,MAAM,cAAc,SAAqC;AACvD,UAAM,OAAO,MAAM,KAAK,gBAAgB;AASxC,UAAM,EAAE,oBAAoB,IAAI,MAAM,OAAO,2BAA2B;AACxE,UAAM,WAAW,MAAM,oBAAoB,oBAAI,QAAQ,GAAG,MAAM,KAAK,QAAQ,OAAO,CAAC;AAErF,QAAI,SAAS,UAAU,KAAK;AAC1B,UAAI;AACF,cAAM,OAAO,MAAM,SAAS,MAAM,EAAE,KAAK;AACzC,gBAAQ,MAAM,6CAA6C,SAAS,QAAQ,IAAI;AAAA,MAClF,QAAQ;AACN,gBAAQ,MAAM,6CAA6C,SAAS,QAAQ,uBAAuB;AAAA,MACrG;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,SAAoC;AACxC,UAAM,OAAO,MAAM,KAAK,gBAAgB;AACxC,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWA,MAAM,iBAA+B;AACnC,UAAM,OAAO,MAAM,KAAK,gBAAgB;AACxC,WAAQ,KAAa;AAAA,EACvB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAYA,kBAAkB;AAEhB,UAAM,kBAAkB,CAAC;AACzB,QAAI,KAAK,OAAO,iBAAiB;AAC/B,iBAAW,CAAC,IAAI,cAAc,KAAK,OAAO,QAAQ,KAAK,OAAO,eAAe,GAAG;AAC9E,YAAI,eAAe,YAAY,OAAO;AAEpC,gBAAM,UAAkC;AAAA,YACtC,QAAQ;AAAA,YACR,QAAQ;AAAA,YACR,WAAW;AAAA,YACX,OAAO;AAAA,YACP,UAAU;AAAA,YACV,SAAS;AAAA,YACT,SAAS;AAAA,YACT,QAAQ;AAAA,YACR,UAAU;AAAA,UACZ;AAEA,0BAAgB,KAAK;AAAA,YACnB;AAAA,YACA,MAAM,QAAQ,EAAE,KAAK,GAAG,OAAO,CAAC,EAAE,YAAY,IAAI,GAAG,MAAM,CAAC;AAAA,YAC5D,SAAS;AAAA,YACT,MAAM;AAAA,UACR,CAAC;AAAA,QACH;AAAA,MACF;AAAA,IACF;AAGA,QAAI,KAAK,OAAO,eAAe,QAAQ;AACrC,iBAAW,KAAK,KAAK,OAAO,eAAe;AACzC,wBAAgB,KAAK;AAAA,UACnB,IAAI,EAAE;AAAA,UACN,MAAM,EAAE,QAAS,EAAE,WAAW,OAAO,CAAC,EAAE,YAAY,IAAI,EAAE,WAAW,MAAM,CAAC;AAAA,UAC5E,SAAS;AAAA,UACT,MAAM;AAAA,QACR,CAAC;AAAA,MACH;AAAA,IACF;AAGA,UAAM,sBAAuD,KAAK,OAAO,oBAAoB,CAAC;AAC9F,UAAM,gBAAgB;AAAA,MACpB,SAAS,oBAAoB,YAAY;AAAA;AAAA,MACzC,eAAe,oBAAoB,iBAAiB;AAAA,MACpD,0BAA0B,oBAAoB,4BAA4B;AAAA,IAC5E;AAGA,UAAM,eAA0C,KAAK,OAAO,WAAW,CAAC;AACxE,UAAM,kBAAkB;AAAA,MACrB,YAAoB,SAAS,KAAK,wBAAwB;AAAA,IAC7D,EAAE,YAAY,MAAM;AACpB,UAAM,WAAW;AAAA,MACf,WAAW,aAAa,aAAa;AAAA,MACrC,UAAU,aAAa,YAAY;AAAA,MACnC,WAAW,aAAa,aAAa;AAAA,MACrC,cAAc,aAAa,gBAAgB;AAAA,MAC3C;AAAA,MACA,cAAc,aAAa,gBAAgB;AAAA,MAC3C,qBAAqB,aAAa,uBAAuB;AAAA,IAC3D;AAEA,WAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOO,gBAAyC;AAC9C,WAAO,KAAK,OAAO;AAAA,EACrB;AACF;;;AC9pCA,sBAmBO;AAEA,IAAM,iBAAiB;AACvB,IAAM,sBAAsB;AAG5B,IAAM,sBAA6B;AAAA,EACxC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAGO,IAAM,2BAA2B;AAAA,EACtC,IAAI;AAAA,EACJ,WAAW;AAAA,EACX,SAAS;AAAA,EACT,MAAM;AAAA,EACN,OAAO;AAAA,EACP,mBAAmB;AAAA,EACnB,MAAM;AAAA,EACN,aAAa;AACf;;;AJ0BO,IAAM,aAAN,MAAmC;AAAA,EASxC,YAAY,UAA6B,CAAC,GAAG;AAR7C,gBAAO;AACP,gBAAO;AACP,mBAAU;AACV,wBAAyB,CAAC,iCAAiC;AAG3D,SAAQ,cAAkC;AAGxC,SAAK,UAAU;AAAA,MACb,gBAAgB;AAAA,MAChB,UAAU;AAAA,MACV,GAAG;AAAA,IACL;AAAA,EACF;AAAA,EAEA,MAAM,KAAK,KAAmC;AAC5C,QAAI,OAAO,KAAK,6BAA6B;AAG7C,QAAI,CAAC,KAAK,QAAQ,QAAQ;AACxB,YAAM,IAAI,MAAM,gCAAgC;AAAA,IAClD;AAGA,UAAM,aAAa,IAAI,WAAgB,MAAM;AAC7C,QAAI,CAAC,YAAY;AACf,UAAI,OAAO,KAAK,gEAAgE;AAAA,IAClF;AAGA,SAAK,cAAc,IAAI,YAAY;AAAA,MACjC,GAAG,KAAK;AAAA,MACR;AAAA,IACF,CAAC;AAGD,QAAI,gBAAgB,QAAQ,KAAK,WAAW;AAE5C,QAAI,WAAuC,UAAU,EAAE,SAAS;AAAA,MAC9D,GAAG;AAAA,MACH,GAAI,KAAK,QAAQ,qBACb,EAAE,mBAAmB,KAAK,QAAQ,mBAAmB,IACrD,CAAC;AAAA,MACL,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA,MAKT,MAAM,CAAC,qBAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAUhB,YAAY,CAAC,mCAAuB;AAAA,IACtC,CAAC;AAED,QAAI,OAAO,KAAK,sCAAsC;AAAA,EACxD;AAAA,EAEA,MAAM,MAAM,KAAmC;AAC7C,QAAI,OAAO,KAAK,yBAAyB;AAEzC,QAAI,CAAC,KAAK,aAAa;AACrB,YAAM,IAAI,MAAM,8BAA8B;AAAA,IAChD;AASA,QAAI,KAAK,gBAAgB,YAAY;AACnC,UAAI;AACF,cAAM,OAAO,IAAI,WAEd,MAAM;AACT,YAAI,SAAS;AACb,mBAAW,CAAC,QAAQ,IAAI,KAAK,OAAO,QAAQ,gCAAoB,GAAG;AACjE,cAAI,QAAQ,OAAO,SAAS,UAAU;AACpC,gBAAI;AACF,mBAAK,iBAAiB,QAAQ,IAA+B;AAC7D;AAAA,YACF,SAAS,KAAU;AACjB,kBAAI,OAAO;AAAA,gBACT,oDAAoD,MAAM,MAAM,KAAK,WAAW,GAAG;AAAA,cACrF;AAAA,YACF;AAAA,UACF;AAAA,QACF;AACA,YAAI,SAAS,GAAG;AACd,cAAI,OAAO;AAAA,YACT,6CAA6C,MAAM,UAAU,SAAS,IAAI,MAAM,EAAE;AAAA,UACpF;AAAA,QACF;AAAA,MACF,QAAQ;AAAA,MAER;AAAA,IACF,CAAC;AAOD,QAAI,KAAK,QAAQ,gBAAgB;AAC/B,UAAI,KAAK,gBAAgB,YAAY;AAKnC,YAAI,KAAK,aAAa;AACpB,cAAI;AACF,kBAAM,WAAW,IAAI,WAAgB,OAAO;AAC5C,gBAAI,UAAU;AACZ,mBAAK,YAAY,gBAAgB,QAAQ;AACzC,kBAAI,OAAO,KAAK,wDAAwD;AAAA,YAC1E;AAAA,UACF,QAAQ;AACN,gBAAI,OAAO,KAAK,qFAAgF;AAAA,UAClG;AAAA,QACF;AAEA,YAAI,aAAiC;AACrC,YAAI;AACF,uBAAa,IAAI,WAAwB,aAAa;AAAA,QACxD,QAAQ;AAAA,QAER;AAEA,YAAI,YAAY;AAKd,gBAAM,iBAAiB;AACvB,cAAI,KAAK,eAAe,OAAO,eAAe,YAAY,YAAY;AACpE,kBAAM,aAAa,eAAe,QAAQ;AAC1C,gBAAI,YAAY;AACd,oBAAM,gBAAgB,KAAK,QAAQ,WAAW;AAC9C,oBAAM,mBAAmB,IAAI,IAAI,aAAa,EAAE;AAChD,oBAAM,YAAY,oBAAoB,UAAU;AAMhD,oBAAM,wBAAwB,iBAAiB,WAAW,kBAAkB;AAC5E,kBAAI,yBAAyB,qBAAqB,WAAW;AAC3D,qBAAK,YAAY,kBAAkB,SAAS;AAC5C,oBAAI,OAAO;AAAA,kBACT,gCAAgC,SAAS,iBAAiB,aAAa;AAAA,gBACzE;AAAA,cACF;AAAA,YACF;AAAA,UACF;AAGA,eAAK,mBAAmB,YAAY,GAAG;AACvC,cAAI,OAAO,KAAK,6BAA6B,KAAK,QAAQ,QAAQ,EAAE;AAAA,QACtE,OAAO;AACL,cAAI,OAAO;AAAA,YACT;AAAA,UAEF;AAAA,QACF;AAAA,MACF,CAAC;AAAA,IACH;AAGA,QAAI;AACF,YAAM,KAAK,IAAI,WAAgB,UAAU;AACzC,UAAI,MAAM,OAAO,GAAG,uBAAuB,YAAY;AACrD,WAAG,mBAAmB,OAAO,OAAY,SAA8B;AAErE,cAAI,MAAM,SAAS,UAAU,MAAM,SAAS,UAAU;AACpD,mBAAO,KAAK;AAAA,UACd;AAEA,gBAAM,KAAK;AAAA,QACb,CAAC;AACD,YAAI,OAAO,KAAK,+CAA+C;AAAA,MACjE;AAAA,IACF,SAAS,IAAI;AACX,UAAI,OAAO,MAAM,sEAAsE;AAAA,IACzF;AAEA,QAAI,OAAO,KAAK,kCAAkC;AAAA,EACpD;AAAA,EAEA,MAAM,UAAyB;AAE7B,SAAK,cAAc;AAAA,EACrB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAcQ,mBAAmB,YAAyB,KAA0B;AAC5E,QAAI,CAAC,KAAK,YAAa;AAEvB,UAAM,WAAW,KAAK,QAAQ,YAAY;AAI1C,QAAI,EAAE,eAAe,eAAe,OAAQ,WAAmB,cAAc,YAAY;AACvF,UAAI,OAAO,MAAM,kFAAkF;AACnG,YAAM,IAAI;AAAA,QACR;AAAA,MAEF;AAAA,IACF;AAEA,UAAM,SAAU,WAAmB,UAAU;AAK7C,WAAO,IAAI,GAAG,QAAQ,WAAW,CAAC,MAAW;AAC3C,UAAI;AACF,cAAM,SAAS,KAAK,YAAa,gBAAgB;AACjD,eAAO,EAAE,KAAK,EAAE,SAAS,MAAM,MAAM,OAAO,CAAC;AAAA,MAC/C,SAAS,OAAO;AACd,cAAM,MAAM,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,KAAK,CAAC;AACpE,eAAO,EAAE,KAAK,EAAE,SAAS,OAAO,OAAO,EAAE,MAAM,qBAAqB,SAAS,IAAI,QAAQ,EAAE,GAAG,GAAG;AAAA,MACnG;AAAA,IACF,CAAC;AAOD,WAAO,IAAI,GAAG,QAAQ,qBAAqB,OAAO,MAAW;AAC3D,UAAI;AACF,cAAM,aAAa,KAAK,YAAa,cAAc;AACnD,YAAI,CAAC,YAAY;AAGf,iBAAO,EAAE,KAAK,EAAE,UAAU,KAAK,CAAC;AAAA,QAClC;AACA,cAAM,QAAQ,MAAM,WAAW,MAAM,YAAY,CAAC,CAAC;AACnD,eAAO,EAAE,KAAK,EAAE,WAAW,SAAS,KAAK,EAAE,CAAC;AAAA,MAC9C,SAAS,OAAO;AACd,YAAI,OAAO,KAAK,qEAAqE,KAAc;AACnG,eAAO,EAAE,KAAK,EAAE,UAAU,KAAK,CAAC;AAAA,MAClC;AAAA,IACF,CAAC;AAiBD,WAAO,KAAK,GAAG,QAAQ,yBAAyB,OAAO,MAAW;AAChE,UAAI;AACF,YAAI,OAAY,CAAC;AACjB,YAAI;AAAE,iBAAO,MAAM,EAAE,IAAI,KAAK;AAAA,QAAG,QAAQ;AAAE,iBAAO,CAAC;AAAA,QAAG;AACtD,cAAM,cAAuB,MAAM;AACnC,YAAI,OAAO,gBAAgB,YAAY,YAAY,WAAW,GAAG;AAC/D,iBAAO,EAAE,KAAK,EAAE,SAAS,OAAO,OAAO,EAAE,MAAM,mBAAmB,SAAS,0BAA0B,EAAE,GAAG,GAAG;AAAA,QAC/G;AAEA,cAAM,UAAU,MAAM,KAAK,YAAa,OAAO;AAC/C,cAAM,UAAU,MAAM,QAAQ,WAAW,EAAE,SAAS,EAAE,IAAI,IAAI,QAAQ,CAAC;AACvE,YAAI,CAAC,SAAS,MAAM,IAAI;AACtB,iBAAO,EAAE,KAAK,EAAE,SAAS,OAAO,OAAO,EAAE,MAAM,gBAAgB,SAAS,gBAAgB,EAAE,GAAG,GAAG;AAAA,QAClG;AACA,cAAM,SAAS,QAAQ,KAAK;AAE5B,cAAM,UAAe,MAAM,KAAK,YAAa,eAAe;AAC5D,YAAI,CAAC,SAAS,mBAAmB,CAAC,SAAS,UAAU;AACnD,iBAAO,EAAE,KAAK,EAAE,SAAS,OAAO,OAAO,EAAE,MAAM,eAAe,SAAS,2BAA2B,EAAE,GAAG,GAAG;AAAA,QAC5G;AAIA,cAAM,SAAS,QAAQ,UAAU,QAAQ,qBAAqB;AAC9D,cAAM,SAAS,QAAQ,UAAU,QAAQ,qBAAqB;AAC9D,YAAI,YAAY,SAAS,QAAQ;AAC/B,iBAAO,EAAE,KAAK,EAAE,SAAS,OAAO,OAAO,EAAE,MAAM,sBAAsB,SAAS,6BAA6B,MAAM,cAAc,EAAE,GAAG,GAAG;AAAA,QACzI;AACA,YAAI,YAAY,SAAS,QAAQ;AAC/B,iBAAO,EAAE,KAAK,EAAE,SAAS,OAAO,OAAO,EAAE,MAAM,qBAAqB,SAAS,4BAA4B,MAAM,cAAc,EAAE,GAAG,GAAG;AAAA,QACvI;AAEA,cAAM,WAAW,MAAM,QAAQ,gBAAgB,aAAa,MAAM;AAClE,cAAM,qBAAqB,UAAU,OAAO,CAAC,MAAW,EAAE,eAAe,gBAAgB,EAAE,QAAQ;AACnG,YAAI,oBAAoB;AAEtB,iBAAO,EAAE,KAAK;AAAA,YACZ,SAAS;AAAA,YACT,OAAO;AAAA,cACL,MAAM;AAAA,cACN,SAAS;AAAA,YACX;AAAA,UACF,GAAG,GAAG;AAAA,QACR;AAEA,cAAM,eAAe,MAAM,QAAQ,SAAS,KAAK,WAAW;AAC5D,cAAM,QAAQ,gBAAgB,cAAc;AAAA,UAC1C;AAAA,UACA,YAAY;AAAA,UACZ,WAAW;AAAA,UACX,UAAU;AAAA,QACZ,CAAC;AAED,eAAO,EAAE,KAAK,EAAE,SAAS,KAAK,CAAC;AAAA,MACjC,SAAS,OAAO;AACd,cAAM,MAAM,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,KAAK,CAAC;AACpE,YAAI,OAAO,MAAM,4CAA4C,GAAG;AAChE,eAAO,EAAE,KAAK,EAAE,SAAS,OAAO,OAAO,EAAE,MAAM,YAAY,SAAS,IAAI,QAAQ,EAAE,GAAG,GAAG;AAAA,MAC1F;AAAA,IACF,CAAC;AAqBD,WAAO,KAAK,GAAG,QAAQ,iCAAiC,OAAO,MAAW;AACxE,UAAI;AACF,YAAI,OAAY,CAAC;AACjB,YAAI;AAAE,iBAAO,MAAM,EAAE,IAAI,KAAK;AAAA,QAAG,QAAQ;AAAE,iBAAO,CAAC;AAAA,QAAG;AACtD,cAAM,WAAoB,MAAM;AAChC,cAAM,WAAoB,MAAM;AAChC,YAAI,OAAO,aAAa,YAAY,SAAS,WAAW,GAAG;AACzD,iBAAO,EAAE,KAAK,EAAE,SAAS,OAAO,OAAO,EAAE,MAAM,mBAAmB,SAAS,wBAAwB,EAAE,GAAG,GAAG;AAAA,QAC7G;AACA,YAAI,OAAO,aAAa,WAAW;AACjC,iBAAO,EAAE,KAAK,EAAE,SAAS,OAAO,OAAO,EAAE,MAAM,mBAAmB,SAAS,6BAA6B,EAAE,GAAG,GAAG;AAAA,QAClH;AAEA,cAAM,UAAU,MAAM,KAAK,YAAa,OAAO;AAC/C,cAAM,UAAU,MAAM,QAAQ,WAAW,EAAE,SAAS,EAAE,IAAI,IAAI,QAAQ,CAAC;AACvE,YAAI,CAAC,SAAS,MAAM,IAAI;AACtB,iBAAO,EAAE,KAAK,EAAE,SAAS,OAAO,OAAO,EAAE,MAAM,gBAAgB,SAAS,gBAAgB,EAAE,GAAG,GAAG;AAAA,QAClG;AAIA,YAAK,QAAQ,KAAa,SAAS,SAAS;AAC1C,iBAAO,EAAE,KAAK,EAAE,SAAS,OAAO,OAAO,EAAE,MAAM,aAAa,SAAS,sBAAsB,EAAE,GAAG,GAAG;AAAA,QACrG;AAUA,cAAM,aAAkB,KAAK,YAAa,cAAc;AACxD,YAAI,CAAC,YAAY;AACf,iBAAO,EAAE,KAAK,EAAE,SAAS,OAAO,OAAO,EAAE,MAAM,eAAe,SAAS,0BAA0B,EAAE,GAAG,GAAG;AAAA,QAC3G;AAEA,cAAM,WAAW,MAAM,WAAW,QAAQ,yBAAyB;AAAA,UACjE,OAAO,EAAE,WAAW,SAAS;AAAA,QAC/B,CAAC;AACD,YAAI,CAAC,UAAU;AACb,iBAAO,EAAE,KAAK,EAAE,SAAS,OAAO,OAAO,EAAE,MAAM,aAAa,SAAS,yBAAyB,EAAE,GAAG,GAAG;AAAA,QACxG;AAEA,cAAM,UAAU,MAAM,WAAW,OAAO,yBAAyB;AAAA,UAC/D,IAAI,SAAS;AAAA,UACb;AAAA,UACA,YAAY,IAAI,KAAK,KAAK,MAAM,KAAK,IAAI,IAAI,GAAI,IAAI,GAAI;AAAA,QAC3D,CAAC;AACD,YAAI,CAAC,SAAS;AACZ,iBAAO,EAAE,KAAK,EAAE,SAAS,OAAO,OAAO,EAAE,MAAM,YAAY,SAAS,gCAAgC,EAAE,GAAG,GAAG;AAAA,QAC9G;AAEA,eAAO,EAAE,KAAK;AAAA,UACZ,SAAS;AAAA,UACT,MAAM;AAAA,YACJ,WAAW;AAAA,YACX;AAAA,UACF;AAAA,QACF,CAAC;AAAA,MACH,SAAS,OAAO;AACd,cAAM,MAAM,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,KAAK,CAAC;AACpE,YAAI,OAAO,MAAM,uCAAuC,GAAG;AAC3D,eAAO,EAAE,KAAK,EAAE,SAAS,OAAO,OAAO,EAAE,MAAM,YAAY,SAAS,IAAI,QAAQ,EAAE,GAAG,GAAG;AAAA,MAC1F;AAAA,IACF,CAAC;AAKD,WAAO,IAAI,GAAG,QAAQ,MAAM,OAAO,MAAW;AAC5C,UAAI;AAEF,cAAM,WAAW,MAAM,KAAK,YAAa,cAAc,EAAE,IAAI,GAAG;AAKhE,YAAI,SAAS,UAAU,KAAK;AAC1B,cAAI;AACF,kBAAM,OAAO,MAAM,SAAS,MAAM,EAAE,KAAK;AACzC,gBAAI,OAAO,MAAM,kDAAkD,IAAI,MAAM,QAAQ,SAAS,MAAM,KAAK,IAAI,EAAE,CAAC;AAAA,UAClH,QAAQ;AACN,gBAAI,OAAO,MAAM,kDAAkD,IAAI,MAAM,QAAQ,SAAS,MAAM,yBAAyB,CAAC;AAAA,UAChI;AAAA,QACF;AAUA,YAAI;AACF,gBAAM,MAAM,EAAE,IAAI;AAClB,cAAI,SAAS,MAAM,eAAe,KAAK,GAAG,GAAG;AAC3C,kBAAM,WAAW,SAAS,QAAQ,IAAI,eAAe;AACrD,gBAAI,CAAC,UAAU;AACb,uBAAS,QAAQ;AAAA,gBACf;AAAA,gBACA;AAAA,cACF;AAAA,YACF;AAAA,UACF;AAAA,QACF,QAAQ;AAAA,QAAsC;AAE9C,eAAO;AAAA,MACT,SAAS,OAAO;AACd,cAAM,MAAM,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,KAAK,CAAC;AACpE,YAAI,OAAO,MAAM,uBAAuB,GAAG;AAG3C,eAAO,IAAI;AAAA,UACT,KAAK,UAAU;AAAA,YACb,SAAS;AAAA,YACT,OAAO,IAAI;AAAA,UACb,CAAC;AAAA,UACD;AAAA,YACE,QAAQ;AAAA,YACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,UAChD;AAAA,QACF;AAAA,MACF;AAAA,IACF,CAAC;AASD,QAAI,KAAK,QAAQ,SAAS,cAAc;AACtC,WAAK,KAAK,4BAA4B,QAAQ,GAAG,EAAE,MAAM,CAAC,UAAU;AAClE,YAAI,OAAO,MAAM,4CAA4C,KAAc;AAAA,MAC7E,CAAC;AAAA,IACH;AAEA,QAAI,OAAO,KAAK,8CAA8C,QAAQ,6BAA6B;AAAA,EACrG;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAc,4BAA4B,QAAa,KAAmC;AACxF,UAAM,OAAO,MAAM,KAAK,YAAa,gBAAgB;AACrD,UAAM,EAAE,iCAAiC,kCAAkC,IAAI,MAAM,OACnF,6BACF;AAEA,UAAM,oBAAoB,gCAAgC,IAAW;AACrE,UAAM,sBAAsB,kCAAkC,IAAW;AAMzE,UAAM,kBAAkB;AACxB,UAAM,qBAAqB,OAAO,SAAyD,QAAoC;AAC7H,YAAM,OAAO,MAAM,QAAQ,GAAG;AAC9B,UAAI;AACF,YAAI,KAAK,MAAM,CAAC,KAAK,QAAQ,IAAI,eAAe,GAAG;AACjD,eAAK,QAAQ,IAAI,iBAAiB,eAAe;AAAA,QACnD;AAAA,MACF,QAAQ;AAAA,MAAoB;AAC5B,aAAO;AAAA,IACT;AAEA,WAAO,IAAI,2CAA2C,CAAC,MAAW,mBAAmB,mBAAmB,EAAE,IAAI,GAAG,CAAC;AAClH,WAAO,IAAI,qCAAqC,CAAC,MAAW,mBAAmB,qBAAqB,EAAE,IAAI,GAAG,CAAC;AAE9G,QAAI,OAAO;AAAA,MACT;AAAA,IACF;AAAA,EACF;AACF;","names":["import_system","organization","dataEngine"]}