@better-auth/sso 1.5.0-beta.1 → 1.5.0-beta.10
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/.turbo/turbo-build.log +13 -9
- package/LICENSE.md +15 -12
- package/dist/client.d.mts +7 -2
- package/dist/client.mjs +7 -2
- package/dist/client.mjs.map +1 -0
- package/dist/{index-CvpS40sl.d.mts → index-CBBJTszO.d.mts} +429 -19
- package/dist/index.d.mts +2 -2
- package/dist/index.mjs +1107 -489
- package/dist/index.mjs.map +1 -0
- package/package.json +17 -14
- package/src/client.ts +5 -1
- package/src/constants.ts +16 -0
- package/src/index.ts +55 -6
- package/src/linking/org-assignment.test.ts +1 -1
- package/src/linking/org-assignment.ts +20 -13
- package/src/oidc.test.ts +113 -1
- package/src/providers.test.ts +1326 -0
- package/src/routes/providers.ts +565 -0
- package/src/routes/schemas.ts +96 -0
- package/src/routes/sso.ts +285 -65
- package/src/saml/algorithms.ts +1 -31
- package/src/saml/assertions.test.ts +239 -0
- package/src/saml/assertions.ts +62 -0
- package/src/saml/index.ts +2 -0
- package/src/saml/parser.ts +56 -0
- package/src/saml-state.ts +78 -0
- package/src/saml.test.ts +2133 -422
- package/src/types.ts +20 -0
- package/src/utils.test.ts +103 -0
- package/src/utils.ts +45 -5
- package/tsconfig.json +3 -0
- package/tsdown.config.ts +1 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.mjs","names":["z","APIError"],"sources":["../src/utils.ts","../src/linking/org-assignment.ts","../src/routes/domain-verification.ts","../src/constants.ts","../src/saml/parser.ts","../src/saml/algorithms.ts","../src/saml/assertions.ts","../src/routes/schemas.ts","../src/routes/providers.ts","../src/oidc/types.ts","../src/oidc/discovery.ts","../src/oidc/errors.ts","../src/saml-state.ts","../src/routes/sso.ts","../src/index.ts"],"sourcesContent":["import { X509Certificate } from \"node:crypto\";\n\n/**\n * Safely parses a value that might be a JSON string or already a parsed object.\n * This handles cases where ORMs like Drizzle might return already parsed objects\n * instead of JSON strings from TEXT/JSON columns.\n *\n * @param value - The value to parse (string, object, null, or undefined)\n * @returns The parsed object or null\n * @throws Error if string parsing fails\n */\nexport function safeJsonParse<T>(\n\tvalue: string | T | null | undefined,\n): T | null {\n\tif (!value) return null;\n\n\tif (typeof value === \"object\") {\n\t\treturn value as T;\n\t}\n\n\tif (typeof value === \"string\") {\n\t\ttry {\n\t\t\treturn JSON.parse(value) as T;\n\t\t} catch (error) {\n\t\t\tthrow new Error(\n\t\t\t\t`Failed to parse JSON: ${error instanceof Error ? error.message : \"Unknown error\"}`,\n\t\t\t);\n\t\t}\n\t}\n\n\treturn null;\n}\n\n/**\n * Checks if a domain matches any domain in a comma-separated list.\n */\nexport const domainMatches = (searchDomain: string, domainList: string) => {\n\tconst search = searchDomain.toLowerCase();\n\tconst domains = domainList\n\t\t.split(\",\")\n\t\t.map((d) => d.trim().toLowerCase())\n\t\t.filter(Boolean);\n\treturn domains.some((d) => search === d || search.endsWith(`.${d}`));\n};\n\n/**\n * Validates email domain against allowed domain(s).\n * Supports comma-separated domains for multi-domain SSO.\n */\nexport const validateEmailDomain = (email: string, domain: string) => {\n\tconst emailDomain = email.split(\"@\")[1]?.toLowerCase();\n\tif (!emailDomain || !domain) {\n\t\treturn false;\n\t}\n\treturn domainMatches(emailDomain, domain);\n};\n\nexport function parseCertificate(certPem: string) {\n\t// SAML metadata X509Certificate elements contain raw base64 without PEM headers,\n\t// but users may also provide full PEM-formatted certificates. Normalize to PEM.\n\tconst normalized = certPem.includes(\"-----BEGIN\")\n\t\t? certPem\n\t\t: `-----BEGIN CERTIFICATE-----\\n${certPem}\\n-----END CERTIFICATE-----`;\n\n\tconst cert = new X509Certificate(normalized);\n\n\treturn {\n\t\tfingerprintSha256: cert.fingerprint256,\n\t\tnotBefore: cert.validFrom,\n\t\tnotAfter: cert.validTo,\n\t\tpublicKeyAlgorithm:\n\t\t\tcert.publicKey.asymmetricKeyType?.toUpperCase() || \"UNKNOWN\",\n\t};\n}\n\nexport function maskClientId(clientId: string): string {\n\tif (clientId.length <= 4) {\n\t\treturn \"****\";\n\t}\n\treturn `****${clientId.slice(-4)}`;\n}\n","import type { GenericEndpointContext, OAuth2Tokens, User } from \"better-auth\";\nimport type { SSOOptions, SSOProvider } from \"../types\";\nimport { domainMatches } from \"../utils\";\nimport type { NormalizedSSOProfile } from \"./types\";\n\nexport interface OrganizationProvisioningOptions {\n\tdisabled?: boolean;\n\tdefaultRole?: \"member\" | \"admin\";\n\tgetRole?: (data: {\n\t\tuser: User & Record<string, any>;\n\t\tuserInfo: Record<string, any>;\n\t\ttoken?: OAuth2Tokens;\n\t\tprovider: SSOProvider<SSOOptions>;\n\t}) => Promise<\"member\" | \"admin\">;\n}\n\nexport interface AssignOrganizationFromProviderOptions {\n\tuser: User;\n\tprofile: NormalizedSSOProfile;\n\tprovider: SSOProvider<SSOOptions>;\n\ttoken?: OAuth2Tokens;\n\tprovisioningOptions?: OrganizationProvisioningOptions;\n}\n\n/**\n * Assigns a user to an organization based on the SSO provider's organizationId.\n * Used in SSO flows (OIDC, SAML) where the provider is already linked to an org.\n */\nexport async function assignOrganizationFromProvider(\n\tctx: GenericEndpointContext,\n\toptions: AssignOrganizationFromProviderOptions,\n): Promise<void> {\n\tconst { user, profile, provider, token, provisioningOptions } = options;\n\n\tif (!provider.organizationId) {\n\t\treturn;\n\t}\n\n\tif (provisioningOptions?.disabled) {\n\t\treturn;\n\t}\n\n\tif (!ctx.context.hasPlugin(\"organization\")) {\n\t\treturn;\n\t}\n\n\tconst isAlreadyMember = await ctx.context.adapter.findOne({\n\t\tmodel: \"member\",\n\t\twhere: [\n\t\t\t{ field: \"organizationId\", value: provider.organizationId },\n\t\t\t{ field: \"userId\", value: user.id },\n\t\t],\n\t});\n\n\tif (isAlreadyMember) {\n\t\treturn;\n\t}\n\n\tconst role = provisioningOptions?.getRole\n\t\t? await provisioningOptions.getRole({\n\t\t\t\tuser,\n\t\t\t\tuserInfo: profile.rawAttributes || {},\n\t\t\t\ttoken,\n\t\t\t\tprovider,\n\t\t\t})\n\t\t: provisioningOptions?.defaultRole || \"member\";\n\n\tawait ctx.context.adapter.create({\n\t\tmodel: \"member\",\n\t\tdata: {\n\t\t\torganizationId: provider.organizationId,\n\t\t\tuserId: user.id,\n\t\t\trole,\n\t\t\tcreatedAt: new Date(),\n\t\t},\n\t});\n}\n\nexport interface AssignOrganizationByDomainOptions {\n\tuser: User;\n\tprovisioningOptions?: OrganizationProvisioningOptions;\n\tdomainVerification?: {\n\t\tenabled?: boolean;\n\t};\n}\n\n/**\n * Assigns a user to an organization based on their email domain.\n * Looks up SSO providers that match the user's email domain and assigns\n * the user to the associated organization.\n *\n * This enables domain-based org assignment for non-SSO sign-in methods\n * (e.g., Google OAuth with @acme.com email gets added to Acme's org).\n */\nexport async function assignOrganizationByDomain(\n\tctx: GenericEndpointContext,\n\toptions: AssignOrganizationByDomainOptions,\n): Promise<void> {\n\tconst { user, provisioningOptions, domainVerification } = options;\n\n\tif (provisioningOptions?.disabled) {\n\t\treturn;\n\t}\n\n\tif (!ctx.context.hasPlugin(\"organization\")) {\n\t\treturn;\n\t}\n\n\tconst domain = user.email.split(\"@\")[1];\n\tif (!domain) {\n\t\treturn;\n\t}\n\n\t// Support comma-separated domains for multi-domain SSO\n\t// First try exact match (fast path)\n\tconst whereClause: { field: string; value: string | boolean }[] = [\n\t\t{ field: \"domain\", value: domain },\n\t];\n\n\tif (domainVerification?.enabled) {\n\t\twhereClause.push({ field: \"domainVerified\", value: true });\n\t}\n\n\tlet ssoProvider = await ctx.context.adapter.findOne<SSOProvider<SSOOptions>>({\n\t\tmodel: \"ssoProvider\",\n\t\twhere: whereClause,\n\t});\n\n\t// If not found, search all providers for comma-separated domain match\n\tif (!ssoProvider) {\n\t\tconst allProviders = await ctx.context.adapter.findMany<\n\t\t\tSSOProvider<SSOOptions>\n\t\t>({\n\t\t\tmodel: \"ssoProvider\",\n\t\t\twhere: domainVerification?.enabled\n\t\t\t\t? [{ field: \"domainVerified\", value: true }]\n\t\t\t\t: [],\n\t\t});\n\t\tssoProvider =\n\t\t\tallProviders.find((p) => domainMatches(domain, p.domain)) ?? null;\n\t}\n\n\tif (!ssoProvider || !ssoProvider.organizationId) {\n\t\treturn;\n\t}\n\n\tconst isAlreadyMember = await ctx.context.adapter.findOne({\n\t\tmodel: \"member\",\n\t\twhere: [\n\t\t\t{ field: \"organizationId\", value: ssoProvider.organizationId },\n\t\t\t{ field: \"userId\", value: user.id },\n\t\t],\n\t});\n\n\tif (isAlreadyMember) {\n\t\treturn;\n\t}\n\n\tconst role = provisioningOptions?.getRole\n\t\t? await provisioningOptions.getRole({\n\t\t\t\tuser,\n\t\t\t\tuserInfo: {},\n\t\t\t\tprovider: ssoProvider,\n\t\t\t})\n\t\t: provisioningOptions?.defaultRole || \"member\";\n\n\tawait ctx.context.adapter.create({\n\t\tmodel: \"member\",\n\t\tdata: {\n\t\t\torganizationId: ssoProvider.organizationId,\n\t\t\tuserId: user.id,\n\t\t\trole,\n\t\t\tcreatedAt: new Date(),\n\t\t},\n\t});\n}\n","import type { Verification } from \"better-auth\";\nimport {\n\tAPIError,\n\tcreateAuthEndpoint,\n\tsessionMiddleware,\n} from \"better-auth/api\";\nimport { generateRandomString } from \"better-auth/crypto\";\nimport * as z from \"zod/v4\";\nimport type { SSOOptions, SSOProvider } from \"../types\";\n\nconst domainVerificationBodySchema = z.object({\n\tproviderId: z.string(),\n});\n\nexport const requestDomainVerification = (options: SSOOptions) => {\n\treturn createAuthEndpoint(\n\t\t\"/sso/request-domain-verification\",\n\t\t{\n\t\t\tmethod: \"POST\",\n\t\t\tbody: domainVerificationBodySchema,\n\t\t\tmetadata: {\n\t\t\t\topenapi: {\n\t\t\t\t\tsummary: \"Request a domain verification\",\n\t\t\t\t\tdescription:\n\t\t\t\t\t\t\"Request a domain verification for the given SSO provider\",\n\t\t\t\t\tresponses: {\n\t\t\t\t\t\t\"404\": {\n\t\t\t\t\t\t\tdescription: \"Provider not found\",\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"409\": {\n\t\t\t\t\t\t\tdescription: \"Domain has already been verified\",\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"201\": {\n\t\t\t\t\t\t\tdescription: \"Domain submitted for verification\",\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\tuse: [sessionMiddleware],\n\t\t},\n\t\tasync (ctx) => {\n\t\t\tconst body = ctx.body;\n\t\t\tconst provider = await ctx.context.adapter.findOne<\n\t\t\t\tSSOProvider<SSOOptions>\n\t\t\t>({\n\t\t\t\tmodel: \"ssoProvider\",\n\t\t\t\twhere: [{ field: \"providerId\", value: body.providerId }],\n\t\t\t});\n\n\t\t\tif (!provider) {\n\t\t\t\tthrow new APIError(\"NOT_FOUND\", {\n\t\t\t\t\tmessage: \"Provider not found\",\n\t\t\t\t\tcode: \"PROVIDER_NOT_FOUND\",\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tconst userId = ctx.context.session.user.id;\n\t\t\tlet isOrgMember = true;\n\t\t\tif (provider.organizationId) {\n\t\t\t\tconst membershipsCount = await ctx.context.adapter.count({\n\t\t\t\t\tmodel: \"member\",\n\t\t\t\t\twhere: [\n\t\t\t\t\t\t{ field: \"userId\", value: userId },\n\t\t\t\t\t\t{ field: \"organizationId\", value: provider.organizationId },\n\t\t\t\t\t],\n\t\t\t\t});\n\n\t\t\t\tisOrgMember = membershipsCount > 0;\n\t\t\t}\n\n\t\t\tif (provider.userId !== userId || !isOrgMember) {\n\t\t\t\tthrow new APIError(\"FORBIDDEN\", {\n\t\t\t\t\tmessage:\n\t\t\t\t\t\t\"User must be owner of or belong to the SSO provider organization\",\n\t\t\t\t\tcode: \"INSUFICCIENT_ACCESS\",\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tif (\"domainVerified\" in provider && provider.domainVerified) {\n\t\t\t\tthrow new APIError(\"CONFLICT\", {\n\t\t\t\t\tmessage: \"Domain has already been verified\",\n\t\t\t\t\tcode: \"DOMAIN_VERIFIED\",\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tconst activeVerification =\n\t\t\t\tawait ctx.context.adapter.findOne<Verification>({\n\t\t\t\t\tmodel: \"verification\",\n\t\t\t\t\twhere: [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tfield: \"identifier\",\n\t\t\t\t\t\t\tvalue: options.domainVerification?.tokenPrefix\n\t\t\t\t\t\t\t\t? `${options.domainVerification?.tokenPrefix}-${provider.providerId}`\n\t\t\t\t\t\t\t\t: `better-auth-token-${provider.providerId}`,\n\t\t\t\t\t\t},\n\t\t\t\t\t\t{ field: \"expiresAt\", value: new Date(), operator: \"gt\" },\n\t\t\t\t\t],\n\t\t\t\t});\n\n\t\t\tif (activeVerification) {\n\t\t\t\tctx.setStatus(201);\n\t\t\t\treturn ctx.json({ domainVerificationToken: activeVerification.value });\n\t\t\t}\n\n\t\t\tconst domainVerificationToken = generateRandomString(24);\n\t\t\tawait ctx.context.adapter.create<Verification>({\n\t\t\t\tmodel: \"verification\",\n\t\t\t\tdata: {\n\t\t\t\t\tidentifier: options.domainVerification?.tokenPrefix\n\t\t\t\t\t\t? `${options.domainVerification?.tokenPrefix}-${provider.providerId}`\n\t\t\t\t\t\t: `better-auth-token-${provider.providerId}`,\n\t\t\t\t\tcreatedAt: new Date(),\n\t\t\t\t\tupdatedAt: new Date(),\n\t\t\t\t\tvalue: domainVerificationToken,\n\t\t\t\t\texpiresAt: new Date(Date.now() + 3600 * 24 * 7 * 1000), // 1 week\n\t\t\t\t},\n\t\t\t});\n\n\t\t\tctx.setStatus(201);\n\t\t\treturn ctx.json({\n\t\t\t\tdomainVerificationToken,\n\t\t\t});\n\t\t},\n\t);\n};\n\nexport const verifyDomain = (options: SSOOptions) => {\n\treturn createAuthEndpoint(\n\t\t\"/sso/verify-domain\",\n\t\t{\n\t\t\tmethod: \"POST\",\n\t\t\tbody: domainVerificationBodySchema,\n\t\t\tmetadata: {\n\t\t\t\topenapi: {\n\t\t\t\t\tsummary: \"Verify the provider domain ownership\",\n\t\t\t\t\tdescription: \"Verify the provider domain ownership via DNS records\",\n\t\t\t\t\tresponses: {\n\t\t\t\t\t\t\"404\": {\n\t\t\t\t\t\t\tdescription: \"Provider not found\",\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"409\": {\n\t\t\t\t\t\t\tdescription:\n\t\t\t\t\t\t\t\t\"Domain has already been verified or no pending verification exists\",\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"502\": {\n\t\t\t\t\t\t\tdescription:\n\t\t\t\t\t\t\t\t\"Unable to verify domain ownership due to upstream validator error\",\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"204\": {\n\t\t\t\t\t\t\tdescription: \"Domain ownership was verified\",\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\tuse: [sessionMiddleware],\n\t\t},\n\t\tasync (ctx) => {\n\t\t\tconst body = ctx.body;\n\t\t\tconst provider = await ctx.context.adapter.findOne<\n\t\t\t\tSSOProvider<SSOOptions>\n\t\t\t>({\n\t\t\t\tmodel: \"ssoProvider\",\n\t\t\t\twhere: [{ field: \"providerId\", value: body.providerId }],\n\t\t\t});\n\n\t\t\tif (!provider) {\n\t\t\t\tthrow new APIError(\"NOT_FOUND\", {\n\t\t\t\t\tmessage: \"Provider not found\",\n\t\t\t\t\tcode: \"PROVIDER_NOT_FOUND\",\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tconst userId = ctx.context.session.user.id;\n\t\t\tlet isOrgMember = true;\n\t\t\tif (provider.organizationId) {\n\t\t\t\tconst membershipsCount = await ctx.context.adapter.count({\n\t\t\t\t\tmodel: \"member\",\n\t\t\t\t\twhere: [\n\t\t\t\t\t\t{ field: \"userId\", value: userId },\n\t\t\t\t\t\t{ field: \"organizationId\", value: provider.organizationId },\n\t\t\t\t\t],\n\t\t\t\t});\n\n\t\t\t\tisOrgMember = membershipsCount > 0;\n\t\t\t}\n\n\t\t\tif (provider.userId !== userId || !isOrgMember) {\n\t\t\t\tthrow new APIError(\"FORBIDDEN\", {\n\t\t\t\t\tmessage:\n\t\t\t\t\t\t\"User must be owner of or belong to the SSO provider organization\",\n\t\t\t\t\tcode: \"INSUFICCIENT_ACCESS\",\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tif (\"domainVerified\" in provider && provider.domainVerified) {\n\t\t\t\tthrow new APIError(\"CONFLICT\", {\n\t\t\t\t\tmessage: \"Domain has already been verified\",\n\t\t\t\t\tcode: \"DOMAIN_VERIFIED\",\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tconst activeVerification =\n\t\t\t\tawait ctx.context.adapter.findOne<Verification>({\n\t\t\t\t\tmodel: \"verification\",\n\t\t\t\t\twhere: [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tfield: \"identifier\",\n\t\t\t\t\t\t\tvalue: options.domainVerification?.tokenPrefix\n\t\t\t\t\t\t\t\t? `${options.domainVerification?.tokenPrefix}-${provider.providerId}`\n\t\t\t\t\t\t\t\t: `better-auth-token-${provider.providerId}`,\n\t\t\t\t\t\t},\n\t\t\t\t\t\t{ field: \"expiresAt\", value: new Date(), operator: \"gt\" },\n\t\t\t\t\t],\n\t\t\t\t});\n\n\t\t\tif (!activeVerification) {\n\t\t\t\tthrow new APIError(\"NOT_FOUND\", {\n\t\t\t\t\tmessage: \"No pending domain verification exists\",\n\t\t\t\t\tcode: \"NO_PENDING_VERIFICATION\",\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tlet records: string[] = [];\n\t\t\tlet dns: typeof import(\"node:dns/promises\");\n\n\t\t\ttry {\n\t\t\t\tdns = await import(\"node:dns/promises\");\n\t\t\t} catch (error) {\n\t\t\t\tctx.context.logger.error(\n\t\t\t\t\t\"The core node:dns module is required for the domain verification feature\",\n\t\t\t\t\terror,\n\t\t\t\t);\n\t\t\t\tthrow new APIError(\"INTERNAL_SERVER_ERROR\", {\n\t\t\t\t\tmessage: \"Unable to verify domain ownership due to server error\",\n\t\t\t\t\tcode: \"DOMAIN_VERIFICATION_FAILED\",\n\t\t\t\t});\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tconst dnsRecords = await dns.resolveTxt(\n\t\t\t\t\tnew URL(provider.domain).hostname,\n\t\t\t\t);\n\t\t\t\trecords = dnsRecords.flat();\n\t\t\t} catch (error) {\n\t\t\t\tctx.context.logger.warn(\n\t\t\t\t\t\"DNS resolution failure while validating domain ownership\",\n\t\t\t\t\terror,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst record = records.find((record) =>\n\t\t\t\trecord.includes(\n\t\t\t\t\t`${activeVerification.identifier}=${activeVerification.value}`,\n\t\t\t\t),\n\t\t\t);\n\t\t\tif (!record) {\n\t\t\t\tthrow new APIError(\"BAD_GATEWAY\", {\n\t\t\t\t\tmessage: \"Unable to verify domain ownership. Try again later\",\n\t\t\t\t\tcode: \"DOMAIN_VERIFICATION_FAILED\",\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tawait ctx.context.adapter.update<SSOProvider<SSOOptions>>({\n\t\t\t\tmodel: \"ssoProvider\",\n\t\t\t\twhere: [{ field: \"providerId\", value: provider.providerId }],\n\t\t\t\tupdate: {\n\t\t\t\t\tdomainVerified: true,\n\t\t\t\t},\n\t\t\t});\n\n\t\t\tctx.setStatus(204);\n\t\t\treturn;\n\t\t},\n\t);\n};\n","/**\n * SAML Constants\n *\n * Centralized constants for SAML SSO functionality.\n */\n\n// ============================================================================\n// Key Prefixes (for verification table storage)\n// ============================================================================\n\n/** Prefix for AuthnRequest IDs used in InResponseTo validation */\nexport const AUTHN_REQUEST_KEY_PREFIX = \"saml-authn-request:\";\n\n/** Prefix for used Assertion IDs used in replay protection */\nexport const USED_ASSERTION_KEY_PREFIX = \"saml-used-assertion:\";\n\n// ============================================================================\n// Time-To-Live (TTL) Defaults\n// ============================================================================\n\n/**\n * Default TTL for AuthnRequest records (5 minutes).\n * This should be sufficient for most IdPs while protecting against stale requests.\n */\nexport const DEFAULT_AUTHN_REQUEST_TTL_MS = 5 * 60 * 1000;\n\n/**\n * Default TTL for used assertion records (15 minutes).\n * This should match the maximum expected NotOnOrAfter window plus clock skew.\n */\nexport const DEFAULT_ASSERTION_TTL_MS = 15 * 60 * 1000;\n\n/**\n * Default clock skew tolerance (5 minutes).\n * Allows for minor time differences between IdP and SP servers.\n *\n * Accommodates:\n * - Network latency and processing time\n * - Clock synchronization differences (NTP drift)\n * - Distributed systems across timezones\n */\nexport const DEFAULT_CLOCK_SKEW_MS = 5 * 60 * 1000;\n\n// ============================================================================\n// Size Limits (DoS Protection)\n// ============================================================================\n\n/**\n * Default maximum size for SAML responses (256 KB).\n * Protects against memory exhaustion from oversized SAML payloads.\n */\nexport const DEFAULT_MAX_SAML_RESPONSE_SIZE = 256 * 1024;\n\n/**\n * Default maximum size for IdP metadata (100 KB).\n * Protects against oversized metadata documents.\n */\nexport const DEFAULT_MAX_SAML_METADATA_SIZE = 100 * 1024;\n","import { XMLParser } from \"fast-xml-parser\";\n\nexport const xmlParser = new XMLParser({\n\tignoreAttributes: false,\n\tattributeNamePrefix: \"@_\",\n\tremoveNSPrefix: true,\n\tprocessEntities: false,\n});\n\nexport function findNode(obj: unknown, nodeName: string): unknown {\n\tif (!obj || typeof obj !== \"object\") return null;\n\n\tconst record = obj as Record<string, unknown>;\n\n\tif (nodeName in record) {\n\t\treturn record[nodeName];\n\t}\n\n\tfor (const value of Object.values(record)) {\n\t\tif (Array.isArray(value)) {\n\t\t\tfor (const item of value) {\n\t\t\t\tconst found = findNode(item, nodeName);\n\t\t\t\tif (found) return found;\n\t\t\t}\n\t\t} else if (typeof value === \"object\" && value !== null) {\n\t\t\tconst found = findNode(value, nodeName);\n\t\t\tif (found) return found;\n\t\t}\n\t}\n\n\treturn null;\n}\n\nexport function countAllNodes(obj: unknown, nodeName: string): number {\n\tif (!obj || typeof obj !== \"object\") return 0;\n\n\tlet count = 0;\n\tconst record = obj as Record<string, unknown>;\n\n\tif (nodeName in record) {\n\t\tconst node = record[nodeName];\n\t\tcount += Array.isArray(node) ? node.length : 1;\n\t}\n\n\tfor (const value of Object.values(record)) {\n\t\tif (Array.isArray(value)) {\n\t\t\tfor (const item of value) {\n\t\t\t\tcount += countAllNodes(item, nodeName);\n\t\t\t}\n\t\t} else if (typeof value === \"object\" && value !== null) {\n\t\t\tcount += countAllNodes(value, nodeName);\n\t\t}\n\t}\n\n\treturn count;\n}\n","import { APIError } from \"better-auth/api\";\nimport { findNode, xmlParser } from \"./parser\";\n\nexport const SignatureAlgorithm = {\n\tRSA_SHA1: \"http://www.w3.org/2000/09/xmldsig#rsa-sha1\",\n\tRSA_SHA256: \"http://www.w3.org/2001/04/xmldsig-more#rsa-sha256\",\n\tRSA_SHA384: \"http://www.w3.org/2001/04/xmldsig-more#rsa-sha384\",\n\tRSA_SHA512: \"http://www.w3.org/2001/04/xmldsig-more#rsa-sha512\",\n\tECDSA_SHA256: \"http://www.w3.org/2001/04/xmldsig-more#ecdsa-sha256\",\n\tECDSA_SHA384: \"http://www.w3.org/2001/04/xmldsig-more#ecdsa-sha384\",\n\tECDSA_SHA512: \"http://www.w3.org/2001/04/xmldsig-more#ecdsa-sha512\",\n} as const;\n\nexport const DigestAlgorithm = {\n\tSHA1: \"http://www.w3.org/2000/09/xmldsig#sha1\",\n\tSHA256: \"http://www.w3.org/2001/04/xmlenc#sha256\",\n\tSHA384: \"http://www.w3.org/2001/04/xmldsig-more#sha384\",\n\tSHA512: \"http://www.w3.org/2001/04/xmlenc#sha512\",\n} as const;\n\nexport const KeyEncryptionAlgorithm = {\n\tRSA_1_5: \"http://www.w3.org/2001/04/xmlenc#rsa-1_5\",\n\tRSA_OAEP: \"http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p\",\n\tRSA_OAEP_SHA256: \"http://www.w3.org/2009/xmlenc11#rsa-oaep\",\n} as const;\n\nexport const DataEncryptionAlgorithm = {\n\tTRIPLEDES_CBC: \"http://www.w3.org/2001/04/xmlenc#tripledes-cbc\",\n\tAES_128_CBC: \"http://www.w3.org/2001/04/xmlenc#aes128-cbc\",\n\tAES_192_CBC: \"http://www.w3.org/2001/04/xmlenc#aes192-cbc\",\n\tAES_256_CBC: \"http://www.w3.org/2001/04/xmlenc#aes256-cbc\",\n\tAES_128_GCM: \"http://www.w3.org/2009/xmlenc11#aes128-gcm\",\n\tAES_192_GCM: \"http://www.w3.org/2009/xmlenc11#aes192-gcm\",\n\tAES_256_GCM: \"http://www.w3.org/2009/xmlenc11#aes256-gcm\",\n} as const;\n\nconst DEPRECATED_SIGNATURE_ALGORITHMS: readonly string[] = [\n\tSignatureAlgorithm.RSA_SHA1,\n];\n\nconst DEPRECATED_KEY_ENCRYPTION_ALGORITHMS: readonly string[] = [\n\tKeyEncryptionAlgorithm.RSA_1_5,\n];\n\nconst DEPRECATED_DATA_ENCRYPTION_ALGORITHMS: readonly string[] = [\n\tDataEncryptionAlgorithm.TRIPLEDES_CBC,\n];\n\nconst DEPRECATED_DIGEST_ALGORITHMS: readonly string[] = [DigestAlgorithm.SHA1];\n\nconst SECURE_SIGNATURE_ALGORITHMS: readonly string[] = [\n\tSignatureAlgorithm.RSA_SHA256,\n\tSignatureAlgorithm.RSA_SHA384,\n\tSignatureAlgorithm.RSA_SHA512,\n\tSignatureAlgorithm.ECDSA_SHA256,\n\tSignatureAlgorithm.ECDSA_SHA384,\n\tSignatureAlgorithm.ECDSA_SHA512,\n];\n\nconst SECURE_DIGEST_ALGORITHMS: readonly string[] = [\n\tDigestAlgorithm.SHA256,\n\tDigestAlgorithm.SHA384,\n\tDigestAlgorithm.SHA512,\n];\n\nconst SHORT_FORM_SIGNATURE_TO_URI: Record<string, string> = {\n\tsha1: SignatureAlgorithm.RSA_SHA1,\n\tsha256: SignatureAlgorithm.RSA_SHA256,\n\tsha384: SignatureAlgorithm.RSA_SHA384,\n\tsha512: SignatureAlgorithm.RSA_SHA512,\n\t\"rsa-sha1\": SignatureAlgorithm.RSA_SHA1,\n\t\"rsa-sha256\": SignatureAlgorithm.RSA_SHA256,\n\t\"rsa-sha384\": SignatureAlgorithm.RSA_SHA384,\n\t\"rsa-sha512\": SignatureAlgorithm.RSA_SHA512,\n\t\"ecdsa-sha256\": SignatureAlgorithm.ECDSA_SHA256,\n\t\"ecdsa-sha384\": SignatureAlgorithm.ECDSA_SHA384,\n\t\"ecdsa-sha512\": SignatureAlgorithm.ECDSA_SHA512,\n};\n\nconst SHORT_FORM_DIGEST_TO_URI: Record<string, string> = {\n\tsha1: DigestAlgorithm.SHA1,\n\tsha256: DigestAlgorithm.SHA256,\n\tsha384: DigestAlgorithm.SHA384,\n\tsha512: DigestAlgorithm.SHA512,\n};\n\nfunction normalizeSignatureAlgorithm(alg: string): string {\n\treturn SHORT_FORM_SIGNATURE_TO_URI[alg.toLowerCase()] ?? alg;\n}\n\nfunction normalizeDigestAlgorithm(alg: string): string {\n\treturn SHORT_FORM_DIGEST_TO_URI[alg.toLowerCase()] ?? alg;\n}\n\nexport type DeprecatedAlgorithmBehavior = \"reject\" | \"warn\" | \"allow\";\n\nexport interface AlgorithmValidationOptions {\n\tonDeprecated?: DeprecatedAlgorithmBehavior;\n\tallowedSignatureAlgorithms?: string[];\n\tallowedDigestAlgorithms?: string[];\n\tallowedKeyEncryptionAlgorithms?: string[];\n\tallowedDataEncryptionAlgorithms?: string[];\n}\n\nfunction extractEncryptionAlgorithms(xml: string): {\n\tkeyEncryption: string | null;\n\tdataEncryption: string | null;\n} {\n\ttry {\n\t\tconst parsed = xmlParser.parse(xml);\n\n\t\tconst encryptedKey = findNode(parsed, \"EncryptedKey\") as Record<\n\t\t\tstring,\n\t\t\tunknown\n\t\t> | null;\n\t\tconst keyEncMethod = encryptedKey?.EncryptionMethod as Record<\n\t\t\tstring,\n\t\t\tunknown\n\t\t> | null;\n\t\tconst keyAlg = keyEncMethod?.[\"@_Algorithm\"] as string | undefined;\n\n\t\tconst encryptedData = findNode(parsed, \"EncryptedData\") as Record<\n\t\t\tstring,\n\t\t\tunknown\n\t\t> | null;\n\t\tconst dataEncMethod = encryptedData?.EncryptionMethod as Record<\n\t\t\tstring,\n\t\t\tunknown\n\t\t> | null;\n\t\tconst dataAlg = dataEncMethod?.[\"@_Algorithm\"] as string | undefined;\n\n\t\treturn {\n\t\t\tkeyEncryption: keyAlg || null,\n\t\t\tdataEncryption: dataAlg || null,\n\t\t};\n\t} catch {\n\t\treturn {\n\t\t\tkeyEncryption: null,\n\t\t\tdataEncryption: null,\n\t\t};\n\t}\n}\n\nfunction hasEncryptedAssertion(xml: string): boolean {\n\ttry {\n\t\tconst parsed = xmlParser.parse(xml);\n\t\treturn findNode(parsed, \"EncryptedAssertion\") !== null;\n\t} catch {\n\t\treturn false;\n\t}\n}\n\nfunction handleDeprecatedAlgorithm(\n\tmessage: string,\n\tbehavior: DeprecatedAlgorithmBehavior,\n\terrorCode: string,\n): void {\n\tswitch (behavior) {\n\t\tcase \"reject\":\n\t\t\tthrow new APIError(\"BAD_REQUEST\", {\n\t\t\t\tmessage,\n\t\t\t\tcode: errorCode,\n\t\t\t});\n\t\tcase \"warn\":\n\t\t\tconsole.warn(`[SAML Security Warning] ${message}`);\n\t\t\tbreak;\n\t\tcase \"allow\":\n\t\t\tbreak;\n\t}\n}\n\nfunction validateSignatureAlgorithm(\n\talgorithm: string | null | undefined,\n\toptions: AlgorithmValidationOptions = {},\n): void {\n\tif (!algorithm) {\n\t\treturn;\n\t}\n\n\tconst { onDeprecated = \"warn\", allowedSignatureAlgorithms } = options;\n\n\tif (allowedSignatureAlgorithms) {\n\t\tif (!allowedSignatureAlgorithms.includes(algorithm)) {\n\t\t\tthrow new APIError(\"BAD_REQUEST\", {\n\t\t\t\tmessage: `SAML signature algorithm not in allow-list: ${algorithm}`,\n\t\t\t\tcode: \"SAML_ALGORITHM_NOT_ALLOWED\",\n\t\t\t});\n\t\t}\n\t\treturn;\n\t}\n\n\tif (DEPRECATED_SIGNATURE_ALGORITHMS.includes(algorithm)) {\n\t\thandleDeprecatedAlgorithm(\n\t\t\t`SAML response uses deprecated signature algorithm: ${algorithm}. Please configure your IdP to use SHA-256 or stronger.`,\n\t\t\tonDeprecated,\n\t\t\t\"SAML_DEPRECATED_ALGORITHM\",\n\t\t);\n\t\treturn;\n\t}\n\n\tif (!SECURE_SIGNATURE_ALGORITHMS.includes(algorithm)) {\n\t\tthrow new APIError(\"BAD_REQUEST\", {\n\t\t\tmessage: `SAML signature algorithm not recognized: ${algorithm}`,\n\t\t\tcode: \"SAML_UNKNOWN_ALGORITHM\",\n\t\t});\n\t}\n}\n\nfunction validateEncryptionAlgorithms(\n\talgorithms: { keyEncryption: string | null; dataEncryption: string | null },\n\toptions: AlgorithmValidationOptions = {},\n): void {\n\tconst {\n\t\tonDeprecated = \"warn\",\n\t\tallowedKeyEncryptionAlgorithms,\n\t\tallowedDataEncryptionAlgorithms,\n\t} = options;\n\n\tconst { keyEncryption, dataEncryption } = algorithms;\n\n\tif (keyEncryption) {\n\t\tif (allowedKeyEncryptionAlgorithms) {\n\t\t\tif (!allowedKeyEncryptionAlgorithms.includes(keyEncryption)) {\n\t\t\t\tthrow new APIError(\"BAD_REQUEST\", {\n\t\t\t\t\tmessage: `SAML key encryption algorithm not in allow-list: ${keyEncryption}`,\n\t\t\t\t\tcode: \"SAML_ALGORITHM_NOT_ALLOWED\",\n\t\t\t\t});\n\t\t\t}\n\t\t} else if (DEPRECATED_KEY_ENCRYPTION_ALGORITHMS.includes(keyEncryption)) {\n\t\t\thandleDeprecatedAlgorithm(\n\t\t\t\t`SAML response uses deprecated key encryption algorithm: ${keyEncryption}. Please configure your IdP to use RSA-OAEP.`,\n\t\t\t\tonDeprecated,\n\t\t\t\t\"SAML_DEPRECATED_ALGORITHM\",\n\t\t\t);\n\t\t}\n\t}\n\n\tif (dataEncryption) {\n\t\tif (allowedDataEncryptionAlgorithms) {\n\t\t\tif (!allowedDataEncryptionAlgorithms.includes(dataEncryption)) {\n\t\t\t\tthrow new APIError(\"BAD_REQUEST\", {\n\t\t\t\t\tmessage: `SAML data encryption algorithm not in allow-list: ${dataEncryption}`,\n\t\t\t\t\tcode: \"SAML_ALGORITHM_NOT_ALLOWED\",\n\t\t\t\t});\n\t\t\t}\n\t\t} else if (DEPRECATED_DATA_ENCRYPTION_ALGORITHMS.includes(dataEncryption)) {\n\t\t\thandleDeprecatedAlgorithm(\n\t\t\t\t`SAML response uses deprecated data encryption algorithm: ${dataEncryption}. Please configure your IdP to use AES-GCM.`,\n\t\t\t\tonDeprecated,\n\t\t\t\t\"SAML_DEPRECATED_ALGORITHM\",\n\t\t\t);\n\t\t}\n\t}\n}\n\nexport function validateSAMLAlgorithms(\n\tresponse: { sigAlg?: string | null; samlContent: string },\n\toptions?: AlgorithmValidationOptions,\n): void {\n\tvalidateSignatureAlgorithm(response.sigAlg, options);\n\n\tif (hasEncryptedAssertion(response.samlContent)) {\n\t\tconst encAlgs = extractEncryptionAlgorithms(response.samlContent);\n\t\tvalidateEncryptionAlgorithms(encAlgs, options);\n\t}\n}\n\nexport interface ConfigAlgorithmValidationOptions {\n\tonDeprecated?: DeprecatedAlgorithmBehavior;\n\tallowedSignatureAlgorithms?: string[];\n\tallowedDigestAlgorithms?: string[];\n}\n\nexport function validateConfigAlgorithms(\n\tconfig: {\n\t\tsignatureAlgorithm?: string | undefined;\n\t\tdigestAlgorithm?: string | undefined;\n\t},\n\toptions: ConfigAlgorithmValidationOptions = {},\n): void {\n\tconst {\n\t\tonDeprecated = \"warn\",\n\t\tallowedSignatureAlgorithms,\n\t\tallowedDigestAlgorithms,\n\t} = options;\n\n\tif (config.signatureAlgorithm) {\n\t\tconst normalized = normalizeSignatureAlgorithm(config.signatureAlgorithm);\n\t\tif (allowedSignatureAlgorithms) {\n\t\t\tconst normalizedAllowList = allowedSignatureAlgorithms.map(\n\t\t\t\tnormalizeSignatureAlgorithm,\n\t\t\t);\n\t\t\tif (!normalizedAllowList.includes(normalized)) {\n\t\t\t\tthrow new APIError(\"BAD_REQUEST\", {\n\t\t\t\t\tmessage: `SAML signature algorithm not in allow-list: ${config.signatureAlgorithm}`,\n\t\t\t\t\tcode: \"SAML_ALGORITHM_NOT_ALLOWED\",\n\t\t\t\t});\n\t\t\t}\n\t\t} else if (DEPRECATED_SIGNATURE_ALGORITHMS.includes(normalized)) {\n\t\t\thandleDeprecatedAlgorithm(\n\t\t\t\t`SAML config uses deprecated signature algorithm: ${config.signatureAlgorithm}. Consider using SHA-256 or stronger.`,\n\t\t\t\tonDeprecated,\n\t\t\t\t\"SAML_DEPRECATED_CONFIG_ALGORITHM\",\n\t\t\t);\n\t\t} else if (!SECURE_SIGNATURE_ALGORITHMS.includes(normalized)) {\n\t\t\tthrow new APIError(\"BAD_REQUEST\", {\n\t\t\t\tmessage: `SAML signature algorithm not recognized: ${config.signatureAlgorithm}`,\n\t\t\t\tcode: \"SAML_UNKNOWN_ALGORITHM\",\n\t\t\t});\n\t\t}\n\t}\n\n\tif (config.digestAlgorithm) {\n\t\tconst normalized = normalizeDigestAlgorithm(config.digestAlgorithm);\n\t\tif (allowedDigestAlgorithms) {\n\t\t\tconst normalizedAllowList = allowedDigestAlgorithms.map(\n\t\t\t\tnormalizeDigestAlgorithm,\n\t\t\t);\n\t\t\tif (!normalizedAllowList.includes(normalized)) {\n\t\t\t\tthrow new APIError(\"BAD_REQUEST\", {\n\t\t\t\t\tmessage: `SAML digest algorithm not in allow-list: ${config.digestAlgorithm}`,\n\t\t\t\t\tcode: \"SAML_ALGORITHM_NOT_ALLOWED\",\n\t\t\t\t});\n\t\t\t}\n\t\t} else if (DEPRECATED_DIGEST_ALGORITHMS.includes(normalized)) {\n\t\t\thandleDeprecatedAlgorithm(\n\t\t\t\t`SAML config uses deprecated digest algorithm: ${config.digestAlgorithm}. Consider using SHA-256 or stronger.`,\n\t\t\t\tonDeprecated,\n\t\t\t\t\"SAML_DEPRECATED_CONFIG_ALGORITHM\",\n\t\t\t);\n\t\t} else if (!SECURE_DIGEST_ALGORITHMS.includes(normalized)) {\n\t\t\tthrow new APIError(\"BAD_REQUEST\", {\n\t\t\t\tmessage: `SAML digest algorithm not recognized: ${config.digestAlgorithm}`,\n\t\t\t\tcode: \"SAML_UNKNOWN_ALGORITHM\",\n\t\t\t});\n\t\t}\n\t}\n}\n","import { base64 } from \"@better-auth/utils/base64\";\nimport { APIError } from \"better-auth/api\";\nimport { countAllNodes, xmlParser } from \"./parser\";\n\nexport interface AssertionCounts {\n\tassertions: number;\n\tencryptedAssertions: number;\n\ttotal: number;\n}\n\n/** @lintignore used in tests */\nexport function countAssertions(xml: string): AssertionCounts {\n\tlet parsed: unknown;\n\ttry {\n\t\tparsed = xmlParser.parse(xml);\n\t} catch {\n\t\tthrow new APIError(\"BAD_REQUEST\", {\n\t\t\tmessage: \"Failed to parse SAML response XML\",\n\t\t\tcode: \"SAML_INVALID_XML\",\n\t\t});\n\t}\n\n\tconst assertions = countAllNodes(parsed, \"Assertion\");\n\tconst encryptedAssertions = countAllNodes(parsed, \"EncryptedAssertion\");\n\n\treturn {\n\t\tassertions,\n\t\tencryptedAssertions,\n\t\ttotal: assertions + encryptedAssertions,\n\t};\n}\n\nexport function validateSingleAssertion(samlResponse: string): void {\n\tlet xml: string;\n\ttry {\n\t\txml = new TextDecoder().decode(base64.decode(samlResponse));\n\t\tif (!xml.includes(\"<\")) {\n\t\t\tthrow new Error(\"Not XML\");\n\t\t}\n\t} catch {\n\t\tthrow new APIError(\"BAD_REQUEST\", {\n\t\t\tmessage: \"Invalid base64-encoded SAML response\",\n\t\t\tcode: \"SAML_INVALID_ENCODING\",\n\t\t});\n\t}\n\n\tconst counts = countAssertions(xml);\n\n\tif (counts.total === 0) {\n\t\tthrow new APIError(\"BAD_REQUEST\", {\n\t\t\tmessage: \"SAML response contains no assertions\",\n\t\t\tcode: \"SAML_NO_ASSERTION\",\n\t\t});\n\t}\n\n\tif (counts.total > 1) {\n\t\tthrow new APIError(\"BAD_REQUEST\", {\n\t\t\tmessage: `SAML response contains ${counts.total} assertions, expected exactly 1`,\n\t\t\tcode: \"SAML_MULTIPLE_ASSERTIONS\",\n\t\t});\n\t}\n}\n","import z from \"zod/v4\";\n\nconst oidcMappingSchema = z\n\t.object({\n\t\tid: z.string().optional(),\n\t\temail: z.string().optional(),\n\t\temailVerified: z.string().optional(),\n\t\tname: z.string().optional(),\n\t\timage: z.string().optional(),\n\t\textraFields: z.record(z.string(), z.any()).optional(),\n\t})\n\t.optional();\n\nconst samlMappingSchema = z\n\t.object({\n\t\tid: z.string().optional(),\n\t\temail: z.string().optional(),\n\t\temailVerified: z.string().optional(),\n\t\tname: z.string().optional(),\n\t\tfirstName: z.string().optional(),\n\t\tlastName: z.string().optional(),\n\t\textraFields: z.record(z.string(), z.any()).optional(),\n\t})\n\t.optional();\n\nconst oidcConfigSchema = z.object({\n\tclientId: z.string().optional(),\n\tclientSecret: z.string().optional(),\n\tauthorizationEndpoint: z.string().url().optional(),\n\ttokenEndpoint: z.string().url().optional(),\n\tuserInfoEndpoint: z.string().url().optional(),\n\ttokenEndpointAuthentication: z\n\t\t.enum([\"client_secret_post\", \"client_secret_basic\"])\n\t\t.optional(),\n\tjwksEndpoint: z.string().url().optional(),\n\tdiscoveryEndpoint: z.string().url().optional(),\n\tscopes: z.array(z.string()).optional(),\n\tpkce: z.boolean().optional(),\n\toverrideUserInfo: z.boolean().optional(),\n\tmapping: oidcMappingSchema,\n});\n\nconst samlConfigSchema = z.object({\n\tentryPoint: z.string().url().optional(),\n\tcert: z.string().optional(),\n\tcallbackUrl: z.string().url().optional(),\n\taudience: z.string().optional(),\n\tidpMetadata: z\n\t\t.object({\n\t\t\tmetadata: z.string().optional(),\n\t\t\tentityID: z.string().optional(),\n\t\t\tcert: z.string().optional(),\n\t\t\tprivateKey: z.string().optional(),\n\t\t\tprivateKeyPass: z.string().optional(),\n\t\t\tisAssertionEncrypted: z.boolean().optional(),\n\t\t\tencPrivateKey: z.string().optional(),\n\t\t\tencPrivateKeyPass: z.string().optional(),\n\t\t\tsingleSignOnService: z\n\t\t\t\t.array(\n\t\t\t\t\tz.object({\n\t\t\t\t\t\tBinding: z.string(),\n\t\t\t\t\t\tLocation: z.string().url(),\n\t\t\t\t\t}),\n\t\t\t\t)\n\t\t\t\t.optional(),\n\t\t})\n\t\t.optional(),\n\tspMetadata: z\n\t\t.object({\n\t\t\tmetadata: z.string().optional(),\n\t\t\tentityID: z.string().optional(),\n\t\t\tbinding: z.string().optional(),\n\t\t\tprivateKey: z.string().optional(),\n\t\t\tprivateKeyPass: z.string().optional(),\n\t\t\tisAssertionEncrypted: z.boolean().optional(),\n\t\t\tencPrivateKey: z.string().optional(),\n\t\t\tencPrivateKeyPass: z.string().optional(),\n\t\t})\n\t\t.optional(),\n\twantAssertionsSigned: z.boolean().optional(),\n\tauthnRequestsSigned: z.boolean().optional(),\n\tsignatureAlgorithm: z.string().optional(),\n\tdigestAlgorithm: z.string().optional(),\n\tidentifierFormat: z.string().optional(),\n\tprivateKey: z.string().optional(),\n\tdecryptionPvk: z.string().optional(),\n\tadditionalParams: z.record(z.string(), z.any()).optional(),\n\tmapping: samlMappingSchema,\n});\n\nexport const updateSSOProviderBodySchema = z.object({\n\tissuer: z.string().url().optional(),\n\tdomain: z.string().optional(),\n\toidcConfig: oidcConfigSchema.optional(),\n\tsamlConfig: samlConfigSchema.optional(),\n});\n","import type { AuthContext } from \"better-auth\";\nimport {\n\tAPIError,\n\tcreateAuthEndpoint,\n\tsessionMiddleware,\n} from \"better-auth/api\";\nimport z from \"zod/v4\";\nimport { DEFAULT_MAX_SAML_METADATA_SIZE } from \"../constants\";\nimport { validateConfigAlgorithms } from \"../saml\";\nimport type { Member, OIDCConfig, SAMLConfig, SSOOptions } from \"../types\";\nimport { maskClientId, parseCertificate, safeJsonParse } from \"../utils\";\nimport { updateSSOProviderBodySchema } from \"./schemas\";\n\ninterface SSOProviderRecord {\n\tid: string;\n\tproviderId: string;\n\tissuer: string;\n\tdomain: string;\n\torganizationId?: string | null;\n\tdomainVerified?: boolean;\n\tuserId: string;\n\toidcConfig?: string | null;\n\tsamlConfig?: string | null;\n}\n\nconst ADMIN_ROLES = [\"owner\", \"admin\"];\n\nasync function isOrgAdmin(\n\tctx: {\n\t\tcontext: {\n\t\t\tadapter: {\n\t\t\t\tfindOne: <T>(query: {\n\t\t\t\t\tmodel: string;\n\t\t\t\t\twhere: { field: string; value: string }[];\n\t\t\t\t}) => Promise<T | null>;\n\t\t\t};\n\t\t};\n\t},\n\tuserId: string,\n\torganizationId: string,\n): Promise<boolean> {\n\tconst member = await ctx.context.adapter.findOne<Member>({\n\t\tmodel: \"member\",\n\t\twhere: [\n\t\t\t{ field: \"userId\", value: userId },\n\t\t\t{ field: \"organizationId\", value: organizationId },\n\t\t],\n\t});\n\tif (!member) return false;\n\tconst roles = member.role.split(\",\");\n\treturn roles.some((r) => ADMIN_ROLES.includes(r.trim()));\n}\n\nasync function batchCheckOrgAdmin(\n\tctx: {\n\t\tcontext: AuthContext;\n\t},\n\tuserId: string,\n\torganizationIds: string[],\n): Promise<Set<string>> {\n\tif (organizationIds.length === 0) {\n\t\treturn new Set();\n\t}\n\n\tconst members = await ctx.context.adapter.findMany<Member>({\n\t\tmodel: \"member\",\n\t\twhere: [\n\t\t\t{ field: \"userId\", value: userId },\n\t\t\t{ field: \"organizationId\", value: organizationIds, operator: \"in\" },\n\t\t],\n\t});\n\n\tconst adminOrgIds = new Set<string>();\n\tfor (const member of members) {\n\t\tconst roles = member.role.split(\",\");\n\t\tif (roles.some((r: string) => ADMIN_ROLES.includes(r.trim()))) {\n\t\t\tadminOrgIds.add(member.organizationId);\n\t\t}\n\t}\n\n\treturn adminOrgIds;\n}\n\nfunction sanitizeProvider(\n\tprovider: {\n\t\tproviderId: string;\n\t\tissuer: string;\n\t\tdomain: string;\n\t\torganizationId?: string | null;\n\t\tdomainVerified?: boolean;\n\t\toidcConfig?: string | OIDCConfig | null;\n\t\tsamlConfig?: string | SAMLConfig | null;\n\t},\n\tbaseURL: string,\n) {\n\tlet oidcConfig: OIDCConfig | null = null;\n\tlet samlConfig: SAMLConfig | null = null;\n\n\ttry {\n\t\toidcConfig = safeJsonParse<OIDCConfig>(provider.oidcConfig as string);\n\t} catch {\n\t\toidcConfig = null;\n\t}\n\n\ttry {\n\t\tsamlConfig = safeJsonParse<SAMLConfig>(provider.samlConfig as string);\n\t} catch {\n\t\tsamlConfig = null;\n\t}\n\n\tconst type = samlConfig ? \"saml\" : \"oidc\";\n\n\treturn {\n\t\tproviderId: provider.providerId,\n\t\ttype,\n\t\tissuer: provider.issuer,\n\t\tdomain: provider.domain,\n\t\torganizationId: provider.organizationId || null,\n\t\tdomainVerified: provider.domainVerified ?? false,\n\t\toidcConfig: oidcConfig\n\t\t\t? {\n\t\t\t\t\tdiscoveryEndpoint: oidcConfig.discoveryEndpoint,\n\t\t\t\t\tclientIdLastFour: maskClientId(oidcConfig.clientId),\n\t\t\t\t\tpkce: oidcConfig.pkce,\n\t\t\t\t\tauthorizationEndpoint: oidcConfig.authorizationEndpoint,\n\t\t\t\t\ttokenEndpoint: oidcConfig.tokenEndpoint,\n\t\t\t\t\tuserInfoEndpoint: oidcConfig.userInfoEndpoint,\n\t\t\t\t\tjwksEndpoint: oidcConfig.jwksEndpoint,\n\t\t\t\t\tscopes: oidcConfig.scopes,\n\t\t\t\t\ttokenEndpointAuthentication: oidcConfig.tokenEndpointAuthentication,\n\t\t\t\t}\n\t\t\t: undefined,\n\t\tsamlConfig: samlConfig\n\t\t\t? {\n\t\t\t\t\tentryPoint: samlConfig.entryPoint,\n\t\t\t\t\tcallbackUrl: samlConfig.callbackUrl,\n\t\t\t\t\taudience: samlConfig.audience,\n\t\t\t\t\twantAssertionsSigned: samlConfig.wantAssertionsSigned,\n\t\t\t\t\tauthnRequestsSigned: samlConfig.authnRequestsSigned,\n\t\t\t\t\tidentifierFormat: samlConfig.identifierFormat,\n\t\t\t\t\tsignatureAlgorithm: samlConfig.signatureAlgorithm,\n\t\t\t\t\tdigestAlgorithm: samlConfig.digestAlgorithm,\n\t\t\t\t\tcertificate: (() => {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\treturn parseCertificate(samlConfig.cert);\n\t\t\t\t\t\t} catch {\n\t\t\t\t\t\t\treturn { error: \"Failed to parse certificate\" };\n\t\t\t\t\t\t}\n\t\t\t\t\t})(),\n\t\t\t\t}\n\t\t\t: undefined,\n\t\tspMetadataUrl: `${baseURL}/sso/saml2/sp/metadata?providerId=${encodeURIComponent(provider.providerId)}`,\n\t};\n}\n\nexport const listSSOProviders = () => {\n\treturn createAuthEndpoint(\n\t\t\"/sso/providers\",\n\t\t{\n\t\t\tmethod: \"GET\",\n\t\t\tuse: [sessionMiddleware],\n\t\t\tmetadata: {\n\t\t\t\topenapi: {\n\t\t\t\t\toperationId: \"listSSOProviders\",\n\t\t\t\t\tsummary: \"List SSO providers\",\n\t\t\t\t\tdescription: \"Returns a list of SSO providers the user has access to\",\n\t\t\t\t\tresponses: {\n\t\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\tdescription: \"List of SSO providers\",\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\tasync (ctx) => {\n\t\t\tconst userId = ctx.context.session.user.id;\n\n\t\t\tconst allProviders =\n\t\t\t\tawait ctx.context.adapter.findMany<SSOProviderRecord>({\n\t\t\t\t\tmodel: \"ssoProvider\",\n\t\t\t\t});\n\n\t\t\tconst userOwnedProviders = allProviders.filter(\n\t\t\t\t(p) => p.userId === userId && !p.organizationId,\n\t\t\t);\n\n\t\t\tconst orgProviders = allProviders.filter(\n\t\t\t\t(p) => p.organizationId !== null && p.organizationId !== undefined,\n\t\t\t);\n\n\t\t\tconst orgPluginEnabled = ctx.context.hasPlugin(\"organization\");\n\n\t\t\tlet accessibleProviders: typeof userOwnedProviders = [\n\t\t\t\t...userOwnedProviders,\n\t\t\t];\n\n\t\t\tif (orgPluginEnabled && orgProviders.length > 0) {\n\t\t\t\tconst orgIds = [\n\t\t\t\t\t...new Set(\n\t\t\t\t\t\torgProviders\n\t\t\t\t\t\t\t.map((p) => p.organizationId)\n\t\t\t\t\t\t\t.filter((id): id is string => id !== null && id !== undefined),\n\t\t\t\t\t),\n\t\t\t\t];\n\n\t\t\t\tconst adminOrgIds = await batchCheckOrgAdmin(ctx, userId, orgIds);\n\n\t\t\t\tconst orgAccessibleProviders = orgProviders.filter(\n\t\t\t\t\t(provider) =>\n\t\t\t\t\t\tprovider.organizationId && adminOrgIds.has(provider.organizationId),\n\t\t\t\t);\n\n\t\t\t\taccessibleProviders = [\n\t\t\t\t\t...accessibleProviders,\n\t\t\t\t\t...orgAccessibleProviders,\n\t\t\t\t];\n\t\t\t} else if (!orgPluginEnabled) {\n\t\t\t\tconst userOwnedOrgProviders = orgProviders.filter(\n\t\t\t\t\t(p) => p.userId === userId,\n\t\t\t\t);\n\t\t\t\taccessibleProviders = [\n\t\t\t\t\t...accessibleProviders,\n\t\t\t\t\t...userOwnedOrgProviders,\n\t\t\t\t];\n\t\t\t}\n\n\t\t\tconst providers = accessibleProviders.map((p) =>\n\t\t\t\tsanitizeProvider(p, ctx.context.baseURL),\n\t\t\t);\n\n\t\t\treturn ctx.json({ providers });\n\t\t},\n\t);\n};\n\nconst getSSOProviderParamsSchema = z.object({\n\tproviderId: z.string(),\n});\n\nasync function checkProviderAccess(\n\tctx: {\n\t\tcontext: AuthContext & {\n\t\t\tsession: { user: { id: string } };\n\t\t};\n\t},\n\tproviderId: string,\n) {\n\tconst userId = ctx.context.session.user.id;\n\n\tconst provider = await ctx.context.adapter.findOne<SSOProviderRecord>({\n\t\tmodel: \"ssoProvider\",\n\t\twhere: [{ field: \"providerId\", value: providerId }],\n\t});\n\n\tif (!provider) {\n\t\tthrow new APIError(\"NOT_FOUND\", {\n\t\t\tmessage: \"Provider not found\",\n\t\t});\n\t}\n\n\tlet hasAccess = false;\n\tif (provider.organizationId) {\n\t\tif (ctx.context.hasPlugin(\"organization\")) {\n\t\t\thasAccess = await isOrgAdmin(ctx, userId, provider.organizationId);\n\t\t} else {\n\t\t\thasAccess = provider.userId === userId;\n\t\t}\n\t} else {\n\t\thasAccess = provider.userId === userId;\n\t}\n\n\tif (!hasAccess) {\n\t\tthrow new APIError(\"FORBIDDEN\", {\n\t\t\tmessage: \"You don't have access to this provider\",\n\t\t});\n\t}\n\n\treturn provider;\n}\n\nexport const getSSOProvider = () => {\n\treturn createAuthEndpoint(\n\t\t\"/sso/providers/:providerId\",\n\t\t{\n\t\t\tmethod: \"GET\",\n\t\t\tuse: [sessionMiddleware],\n\t\t\tparams: getSSOProviderParamsSchema,\n\t\t\tmetadata: {\n\t\t\t\topenapi: {\n\t\t\t\t\toperationId: \"getSSOProvider\",\n\t\t\t\t\tsummary: \"Get SSO provider details\",\n\t\t\t\t\tdescription: \"Returns sanitized details for a specific SSO provider\",\n\t\t\t\t\tresponses: {\n\t\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\tdescription: \"SSO provider details\",\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"404\": {\n\t\t\t\t\t\t\tdescription: \"Provider not found\",\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"403\": {\n\t\t\t\t\t\t\tdescription: \"Access denied\",\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\tasync (ctx) => {\n\t\t\tconst { providerId } = ctx.params;\n\n\t\t\tconst provider = await checkProviderAccess(ctx, providerId);\n\n\t\t\treturn ctx.json(sanitizeProvider(provider, ctx.context.baseURL));\n\t\t},\n\t);\n};\n\nfunction parseAndValidateConfig<T>(\n\tconfigString: string | null | undefined,\n\tconfigType: \"SAML\" | \"OIDC\",\n): T {\n\tlet config: T | null = null;\n\ttry {\n\t\tconfig = safeJsonParse<T>(configString as string);\n\t} catch {\n\t\tconfig = null;\n\t}\n\tif (!config) {\n\t\tthrow new APIError(\"BAD_REQUEST\", {\n\t\t\tmessage: `Cannot update ${configType} config for a provider that doesn't have ${configType} configured`,\n\t\t});\n\t}\n\treturn config;\n}\n\nfunction mergeSAMLConfig(\n\tcurrent: SAMLConfig,\n\tupdates: Partial<SAMLConfig>,\n\tissuer: string,\n): SAMLConfig {\n\treturn {\n\t\t...current,\n\t\t...updates,\n\t\tissuer,\n\t\tentryPoint: updates.entryPoint ?? current.entryPoint,\n\t\tcert: updates.cert ?? current.cert,\n\t\tcallbackUrl: updates.callbackUrl ?? current.callbackUrl,\n\t\tspMetadata: updates.spMetadata ?? current.spMetadata,\n\t\tidpMetadata: updates.idpMetadata ?? current.idpMetadata,\n\t\tmapping: updates.mapping ?? current.mapping,\n\t\taudience: updates.audience ?? current.audience,\n\t\twantAssertionsSigned:\n\t\t\tupdates.wantAssertionsSigned ?? current.wantAssertionsSigned,\n\t\tauthnRequestsSigned:\n\t\t\tupdates.authnRequestsSigned ?? current.authnRequestsSigned,\n\t\tidentifierFormat: updates.identifierFormat ?? current.identifierFormat,\n\t\tsignatureAlgorithm:\n\t\t\tupdates.signatureAlgorithm ?? current.signatureAlgorithm,\n\t\tdigestAlgorithm: updates.digestAlgorithm ?? current.digestAlgorithm,\n\t};\n}\n\nfunction mergeOIDCConfig(\n\tcurrent: OIDCConfig,\n\tupdates: Partial<OIDCConfig>,\n\tissuer: string,\n): OIDCConfig {\n\treturn {\n\t\t...current,\n\t\t...updates,\n\t\tissuer,\n\t\tpkce: updates.pkce ?? current.pkce ?? true,\n\t\tclientId: updates.clientId ?? current.clientId,\n\t\tclientSecret: updates.clientSecret ?? current.clientSecret,\n\t\tdiscoveryEndpoint: updates.discoveryEndpoint ?? current.discoveryEndpoint,\n\t\tmapping: updates.mapping ?? current.mapping,\n\t\tscopes: updates.scopes ?? current.scopes,\n\t\tauthorizationEndpoint:\n\t\t\tupdates.authorizationEndpoint ?? current.authorizationEndpoint,\n\t\ttokenEndpoint: updates.tokenEndpoint ?? current.tokenEndpoint,\n\t\tuserInfoEndpoint: updates.userInfoEndpoint ?? current.userInfoEndpoint,\n\t\tjwksEndpoint: updates.jwksEndpoint ?? current.jwksEndpoint,\n\t\ttokenEndpointAuthentication:\n\t\t\tupdates.tokenEndpointAuthentication ??\n\t\t\tcurrent.tokenEndpointAuthentication,\n\t};\n}\n\nexport const updateSSOProvider = (options: SSOOptions) => {\n\treturn createAuthEndpoint(\n\t\t\"/sso/providers/:providerId\",\n\t\t{\n\t\t\tmethod: \"PATCH\",\n\t\t\tuse: [sessionMiddleware],\n\t\t\tparams: getSSOProviderParamsSchema,\n\t\t\tbody: updateSSOProviderBodySchema,\n\t\t\tmetadata: {\n\t\t\t\topenapi: {\n\t\t\t\t\toperationId: \"updateSSOProvider\",\n\t\t\t\t\tsummary: \"Update SSO provider\",\n\t\t\t\t\tdescription:\n\t\t\t\t\t\t\"Partially update an SSO provider. Only provided fields are updated. If domain changes, domainVerified is reset to false.\",\n\t\t\t\t\tresponses: {\n\t\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\tdescription: \"SSO provider updated successfully\",\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"404\": {\n\t\t\t\t\t\t\tdescription: \"Provider not found\",\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"403\": {\n\t\t\t\t\t\t\tdescription: \"Access denied\",\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\tasync (ctx) => {\n\t\t\tconst { providerId } = ctx.params;\n\t\t\tconst body = ctx.body;\n\n\t\t\tconst { issuer, domain, samlConfig, oidcConfig } = body;\n\t\t\tif (!issuer && !domain && !samlConfig && !oidcConfig) {\n\t\t\t\tthrow new APIError(\"BAD_REQUEST\", {\n\t\t\t\t\tmessage: \"No fields provided for update\",\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tconst existingProvider = await checkProviderAccess(ctx, providerId);\n\n\t\t\tconst updateData: Partial<SSOProviderRecord> = {};\n\n\t\t\tif (body.issuer !== undefined) {\n\t\t\t\tupdateData.issuer = body.issuer;\n\t\t\t}\n\n\t\t\tif (body.domain !== undefined) {\n\t\t\t\tupdateData.domain = body.domain;\n\t\t\t\tif (body.domain !== existingProvider.domain) {\n\t\t\t\t\tupdateData.domainVerified = false;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (body.samlConfig) {\n\t\t\t\tif (body.samlConfig.idpMetadata?.metadata) {\n\t\t\t\t\tconst maxMetadataSize =\n\t\t\t\t\t\toptions?.saml?.maxMetadataSize ?? DEFAULT_MAX_SAML_METADATA_SIZE;\n\t\t\t\t\tif (\n\t\t\t\t\t\tnew TextEncoder().encode(body.samlConfig.idpMetadata.metadata)\n\t\t\t\t\t\t\t.length > maxMetadataSize\n\t\t\t\t\t) {\n\t\t\t\t\t\tthrow new APIError(\"BAD_REQUEST\", {\n\t\t\t\t\t\t\tmessage: `IdP metadata exceeds maximum allowed size (${maxMetadataSize} bytes)`,\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (\n\t\t\t\t\tbody.samlConfig.signatureAlgorithm !== undefined ||\n\t\t\t\t\tbody.samlConfig.digestAlgorithm !== undefined\n\t\t\t\t) {\n\t\t\t\t\tvalidateConfigAlgorithms(\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tsignatureAlgorithm: body.samlConfig.signatureAlgorithm,\n\t\t\t\t\t\t\tdigestAlgorithm: body.samlConfig.digestAlgorithm,\n\t\t\t\t\t\t},\n\t\t\t\t\t\toptions?.saml?.algorithms,\n\t\t\t\t\t);\n\t\t\t\t}\n\n\t\t\t\tconst currentSamlConfig = parseAndValidateConfig<SAMLConfig>(\n\t\t\t\t\texistingProvider.samlConfig,\n\t\t\t\t\t\"SAML\",\n\t\t\t\t);\n\n\t\t\t\tconst updatedSamlConfig = mergeSAMLConfig(\n\t\t\t\t\tcurrentSamlConfig,\n\t\t\t\t\tbody.samlConfig,\n\t\t\t\t\tupdateData.issuer ||\n\t\t\t\t\t\tcurrentSamlConfig.issuer ||\n\t\t\t\t\t\texistingProvider.issuer,\n\t\t\t\t);\n\n\t\t\t\tupdateData.samlConfig = JSON.stringify(updatedSamlConfig);\n\t\t\t}\n\n\t\t\tif (body.oidcConfig) {\n\t\t\t\tconst currentOidcConfig = parseAndValidateConfig<OIDCConfig>(\n\t\t\t\t\texistingProvider.oidcConfig,\n\t\t\t\t\t\"OIDC\",\n\t\t\t\t);\n\n\t\t\t\tconst updatedOidcConfig = mergeOIDCConfig(\n\t\t\t\t\tcurrentOidcConfig,\n\t\t\t\t\tbody.oidcConfig,\n\t\t\t\t\tupdateData.issuer ||\n\t\t\t\t\t\tcurrentOidcConfig.issuer ||\n\t\t\t\t\t\texistingProvider.issuer,\n\t\t\t\t);\n\n\t\t\t\tupdateData.oidcConfig = JSON.stringify(updatedOidcConfig);\n\t\t\t}\n\n\t\t\tawait ctx.context.adapter.update({\n\t\t\t\tmodel: \"ssoProvider\",\n\t\t\t\twhere: [{ field: \"providerId\", value: providerId }],\n\t\t\t\tupdate: updateData,\n\t\t\t});\n\n\t\t\tconst fullProvider = await ctx.context.adapter.findOne<SSOProviderRecord>(\n\t\t\t\t{\n\t\t\t\t\tmodel: \"ssoProvider\",\n\t\t\t\t\twhere: [{ field: \"providerId\", value: providerId }],\n\t\t\t\t},\n\t\t\t);\n\n\t\t\tif (!fullProvider) {\n\t\t\t\tthrow new APIError(\"NOT_FOUND\", {\n\t\t\t\t\tmessage: \"Provider not found after update\",\n\t\t\t\t});\n\t\t\t}\n\n\t\t\treturn ctx.json(sanitizeProvider(fullProvider, ctx.context.baseURL));\n\t\t},\n\t);\n};\n\nexport const deleteSSOProvider = () => {\n\treturn createAuthEndpoint(\n\t\t\"/sso/providers/:providerId\",\n\t\t{\n\t\t\tmethod: \"DELETE\",\n\t\t\tuse: [sessionMiddleware],\n\t\t\tparams: getSSOProviderParamsSchema,\n\t\t\tmetadata: {\n\t\t\t\topenapi: {\n\t\t\t\t\toperationId: \"deleteSSOProvider\",\n\t\t\t\t\tsummary: \"Delete SSO provider\",\n\t\t\t\t\tdescription: \"Deletes an SSO provider\",\n\t\t\t\t\tresponses: {\n\t\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\tdescription: \"SSO provider deleted successfully\",\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"404\": {\n\t\t\t\t\t\t\tdescription: \"Provider not found\",\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"403\": {\n\t\t\t\t\t\t\tdescription: \"Access denied\",\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\tasync (ctx) => {\n\t\t\tconst { providerId } = ctx.params;\n\n\t\t\tawait checkProviderAccess(ctx, providerId);\n\n\t\t\tawait ctx.context.adapter.delete({\n\t\t\t\tmodel: \"ssoProvider\",\n\t\t\t\twhere: [{ field: \"providerId\", value: providerId }],\n\t\t\t});\n\n\t\t\treturn ctx.json({ success: true });\n\t\t},\n\t);\n};\n","/**\n * OIDC Discovery Types\n *\n * Types for the OIDC discovery document and hydrated configuration.\n * Based on OpenID Connect Discovery 1.0 specification.\n *\n * @see https://openid.net/specs/openid-connect-discovery-1_0.html\n */\n\n/**\n * Raw OIDC Discovery Document as returned by the IdP's\n * .well-known/openid-configuration endpoint.\n *\n * Required fields for Better Auth's OIDC support:\n * - issuer\n * - authorization_endpoint\n * - token_endpoint\n * - jwks_uri (required for ID token validation)\n *\n */\nexport interface OIDCDiscoveryDocument {\n\t/** REQUIRED. URL using the https scheme that the OP asserts as its Issuer Identifier. */\n\tissuer: string;\n\n\t/** REQUIRED. URL of the OP's OAuth 2.0 Authorization Endpoint. */\n\tauthorization_endpoint: string;\n\n\t/**\n\t * REQUIRED (spec says \"unless only implicit flow is used\").\n\t * URL of the OP's OAuth 2.0 Token Endpoint.\n\t * We only support authorization code flow.\n\t */\n\ttoken_endpoint: string;\n\n\t/** REQUIRED. URL of the OP's JSON Web Key Set document for ID token validation. */\n\tjwks_uri: string;\n\n\t/** RECOMMENDED. URL of the OP's UserInfo Endpoint. */\n\tuserinfo_endpoint?: string;\n\n\t/**\n\t * OPTIONAL. JSON array containing a list of Client Authentication methods\n\t * supported by this Token Endpoint.\n\t * Default: [\"client_secret_basic\"]\n\t */\n\ttoken_endpoint_auth_methods_supported?: string[];\n\n\t/** OPTIONAL. JSON array containing a list of the OAuth 2.0 scope values that this server supports. */\n\tscopes_supported?: string[];\n\n\t/** OPTIONAL. JSON array containing a list of the OAuth 2.0 response_type values that this OP supports. */\n\tresponse_types_supported?: string[];\n\n\t/** OPTIONAL. JSON array containing a list of the Subject Identifier types that this OP supports. */\n\tsubject_types_supported?: string[];\n\n\t/** OPTIONAL. JSON array containing a list of the JWS signing algorithms supported by the OP. */\n\tid_token_signing_alg_values_supported?: string[];\n\n\t/** OPTIONAL. JSON array containing a list of the claim names that the OP may supply values for. */\n\tclaims_supported?: string[];\n\n\t/** OPTIONAL. URL of a page containing human-readable information about the OP. */\n\tservice_documentation?: string;\n\n\t/** OPTIONAL. Boolean value specifying whether the OP supports use of the claims parameter. */\n\tclaims_parameter_supported?: boolean;\n\n\t/** OPTIONAL. Boolean value specifying whether the OP supports use of the request parameter. */\n\trequest_parameter_supported?: boolean;\n\n\t/** OPTIONAL. Boolean value specifying whether the OP supports use of the request_uri parameter. */\n\trequest_uri_parameter_supported?: boolean;\n\n\t/** OPTIONAL. Boolean value specifying whether the OP requires any request_uri values to be pre-registered. */\n\trequire_request_uri_registration?: boolean;\n\n\t/** OPTIONAL. URL of the OP's end session endpoint. */\n\tend_session_endpoint?: string;\n\n\t/** OPTIONAL. URL of the OP's revocation endpoint. */\n\trevocation_endpoint?: string;\n\n\t/** OPTIONAL. URL of the OP's introspection endpoint. */\n\tintrospection_endpoint?: string;\n\n\t/** OPTIONAL. JSON array of PKCE code challenge methods supported (e.g., \"S256\", \"plain\"). */\n\tcode_challenge_methods_supported?: string[];\n\n\t/** Allow additional fields from the discovery document */\n\t[key: string]: unknown;\n}\n\n/**\n * Error codes for OIDC discovery operations.\n */\nexport type DiscoveryErrorCode =\n\t/** Request to discovery endpoint timed out */\n\t| \"discovery_timeout\"\n\t/** Discovery endpoint returned 404 or similar */\n\t| \"discovery_not_found\"\n\t/** Discovery endpoint returned invalid JSON */\n\t| \"discovery_invalid_json\"\n\t/** Discovery URL is invalid or malformed */\n\t| \"discovery_invalid_url\"\n\t/** Discovery URL is not trusted by the trusted origins configuration */\n\t| \"discovery_untrusted_origin\"\n\t/** Discovery document issuer doesn't match configured issuer */\n\t| \"issuer_mismatch\"\n\t/** Discovery document is missing required fields */\n\t| \"discovery_incomplete\"\n\t/** IdP only advertises token auth methods that Better Auth doesn't currently support */\n\t| \"unsupported_token_auth_method\"\n\t/** Catch-all for unexpected errors */\n\t| \"discovery_unexpected_error\";\n\n/**\n * Custom error class for OIDC discovery failures.\n * Can be caught and mapped to APIError at the edge.\n */\nexport class DiscoveryError extends Error {\n\tpublic readonly code: DiscoveryErrorCode;\n\tpublic readonly details?: Record<string, unknown>;\n\n\tconstructor(\n\t\tcode: DiscoveryErrorCode,\n\t\tmessage: string,\n\t\tdetails?: Record<string, unknown>,\n\t\toptions?: { cause?: unknown },\n\t) {\n\t\tsuper(message, options);\n\t\tthis.name = \"DiscoveryError\";\n\t\tthis.code = code;\n\t\tthis.details = details;\n\n\t\t// Maintains proper stack trace for where the error was thrown\n\t\tif (Error.captureStackTrace) {\n\t\t\tError.captureStackTrace(this, DiscoveryError);\n\t\t}\n\t}\n}\n\n/**\n * Hydrated OIDC configuration after discovery.\n * This is the normalized shape that gets persisted to the database\n * or merged into provider config at runtime.\n *\n * Field names are camelCase to match Better Auth conventions.\n */\nexport interface HydratedOIDCConfig {\n\t/** The issuer URL (validated to match configured issuer) */\n\tissuer: string;\n\n\t/** The discovery endpoint URL */\n\tdiscoveryEndpoint: string;\n\n\t/** URL of the authorization endpoint */\n\tauthorizationEndpoint: string;\n\n\t/** URL of the token endpoint */\n\ttokenEndpoint: string;\n\n\t/** URL of the JWKS endpoint */\n\tjwksEndpoint: string;\n\n\t/** URL of the userinfo endpoint (optional) */\n\tuserInfoEndpoint?: string;\n\n\t/** Token endpoint authentication method */\n\ttokenEndpointAuthentication?: \"client_secret_basic\" | \"client_secret_post\";\n\n\t/** Scopes supported by the IdP */\n\tscopesSupported?: string[];\n}\n\n/**\n * Parameters for the discoverOIDCConfig function.\n */\nexport interface DiscoverOIDCConfigParams {\n\t/** The issuer URL to discover configuration from */\n\tissuer: string;\n\n\t/**\n\t * Optional existing configuration.\n\t * Values provided here will override discovered values.\n\t */\n\texistingConfig?: Partial<HydratedOIDCConfig>;\n\n\t/**\n\t * Optional custom discovery endpoint URL.\n\t * If not provided, defaults to <issuer>/.well-known/openid-configuration\n\t */\n\tdiscoveryEndpoint?: string;\n\n\t/**\n\t * Optional timeout in milliseconds for the discovery request.\n\t * @default 10000 (10 seconds)\n\t */\n\ttimeout?: number;\n\n\t/**\n\t * Trusted origin predicate. See \"trustedOrigins\" option\n\t * @param url the url to test\n\t * @returns {boolean} return true for urls that belong to a trusted origin and false otherwise\n\t */\n\tisTrustedOrigin: (url: string) => boolean;\n}\n\n/**\n * Required fields that must be present in a valid discovery document.\n */\nexport const REQUIRED_DISCOVERY_FIELDS = [\n\t\"issuer\",\n\t\"authorization_endpoint\",\n\t\"token_endpoint\",\n\t\"jwks_uri\",\n] as const;\n\nexport type RequiredDiscoveryField = (typeof REQUIRED_DISCOVERY_FIELDS)[number];\n","/**\n * OIDC Discovery Pipeline\n *\n * Implements OIDC discovery document fetching, validation, and hydration.\n * This module is used both at provider registration time (to persist validated config)\n * and at runtime (to hydrate legacy providers that are missing metadata).\n *\n * @see https://openid.net/specs/openid-connect-discovery-1_0.html\n */\n\nimport { betterFetch } from \"@better-fetch/fetch\";\nimport type {\n\tDiscoverOIDCConfigParams,\n\tHydratedOIDCConfig,\n\tOIDCDiscoveryDocument,\n} from \"./types\";\nimport { DiscoveryError, REQUIRED_DISCOVERY_FIELDS } from \"./types\";\n\n/** Default timeout for discovery requests (10 seconds) */\nconst DEFAULT_DISCOVERY_TIMEOUT = 10000;\n\n/**\n * Main entry point: Discover and hydrate OIDC configuration from an issuer.\n *\n * This function:\n * 1. Computes the discovery URL from the issuer\n * 2. Validates the discovery URL\n * 3. Fetches the discovery document\n * 4. Validates the discovery document (issuer match + required fields)\n * 5. Normalizes URLs\n * 6. Selects token endpoint auth method\n * 7. Merges with existing config (existing values take precedence)\n *\n * @param params - Discovery parameters\n * @param isTrustedOrigin - Origin verification tester function\n * @returns Hydrated OIDC configuration ready for persistence\n * @throws DiscoveryError on any failure\n */\nexport async function discoverOIDCConfig(\n\tparams: DiscoverOIDCConfigParams,\n): Promise<HydratedOIDCConfig> {\n\tconst {\n\t\tissuer,\n\t\texistingConfig,\n\t\ttimeout = DEFAULT_DISCOVERY_TIMEOUT,\n\t} = params;\n\n\tconst discoveryUrl =\n\t\tparams.discoveryEndpoint ||\n\t\texistingConfig?.discoveryEndpoint ||\n\t\tcomputeDiscoveryUrl(issuer);\n\n\tvalidateDiscoveryUrl(discoveryUrl, params.isTrustedOrigin);\n\n\tconst discoveryDoc = await fetchDiscoveryDocument(discoveryUrl, timeout);\n\n\tvalidateDiscoveryDocument(discoveryDoc, issuer);\n\n\tconst normalizedDoc = normalizeDiscoveryUrls(\n\t\tdiscoveryDoc,\n\t\tissuer,\n\t\tparams.isTrustedOrigin,\n\t);\n\n\tconst tokenEndpointAuth = selectTokenEndpointAuthMethod(\n\t\tnormalizedDoc,\n\t\texistingConfig?.tokenEndpointAuthentication,\n\t);\n\n\tconst hydratedConfig: HydratedOIDCConfig = {\n\t\tissuer: existingConfig?.issuer ?? normalizedDoc.issuer,\n\t\tdiscoveryEndpoint: existingConfig?.discoveryEndpoint ?? discoveryUrl,\n\t\tauthorizationEndpoint:\n\t\t\texistingConfig?.authorizationEndpoint ??\n\t\t\tnormalizedDoc.authorization_endpoint,\n\t\ttokenEndpoint:\n\t\t\texistingConfig?.tokenEndpoint ?? normalizedDoc.token_endpoint,\n\t\tjwksEndpoint: existingConfig?.jwksEndpoint ?? normalizedDoc.jwks_uri,\n\t\tuserInfoEndpoint:\n\t\t\texistingConfig?.userInfoEndpoint ?? normalizedDoc.userinfo_endpoint,\n\t\ttokenEndpointAuthentication:\n\t\t\texistingConfig?.tokenEndpointAuthentication ?? tokenEndpointAuth,\n\t\tscopesSupported:\n\t\t\texistingConfig?.scopesSupported ?? normalizedDoc.scopes_supported,\n\t};\n\n\treturn hydratedConfig;\n}\n\n/**\n * Compute the discovery URL from an issuer URL.\n *\n * Per OIDC Discovery spec, the discovery document is located at:\n * <issuer>/.well-known/openid-configuration\n *\n * Handles trailing slashes correctly.\n */\nexport function computeDiscoveryUrl(issuer: string): string {\n\tconst baseUrl = issuer.endsWith(\"/\") ? issuer.slice(0, -1) : issuer;\n\treturn `${baseUrl}/.well-known/openid-configuration`;\n}\n\n/**\n * Validate a discovery URL before fetching.\n *\n * @param url - The discovery URL to validate\n * @param isTrustedOrigin - Origin verification tester function\n * @throws DiscoveryError if URL is invalid\n */\nexport function validateDiscoveryUrl(\n\turl: string,\n\tisTrustedOrigin: DiscoverOIDCConfigParams[\"isTrustedOrigin\"],\n): void {\n\tconst discoveryEndpoint = parseURL(\"discoveryEndpoint\", url).toString();\n\n\tif (!isTrustedOrigin(discoveryEndpoint)) {\n\t\tthrow new DiscoveryError(\n\t\t\t\"discovery_untrusted_origin\",\n\t\t\t`The main discovery endpoint \"${discoveryEndpoint}\" is not trusted by your trusted origins configuration.`,\n\t\t\t{ url: discoveryEndpoint },\n\t\t);\n\t}\n}\n\n/**\n * Fetch the OIDC discovery document from the IdP.\n *\n * @param url - The discovery endpoint URL\n * @param timeout - Request timeout in milliseconds\n * @returns The parsed discovery document\n * @throws DiscoveryError on network errors, timeouts, or invalid responses\n */\nexport async function fetchDiscoveryDocument(\n\turl: string,\n\ttimeout: number = DEFAULT_DISCOVERY_TIMEOUT,\n): Promise<OIDCDiscoveryDocument> {\n\ttry {\n\t\tconst response = await betterFetch<OIDCDiscoveryDocument>(url, {\n\t\t\tmethod: \"GET\",\n\t\t\ttimeout,\n\t\t});\n\n\t\tif (response.error) {\n\t\t\tconst { status } = response.error;\n\n\t\t\tif (status === 404) {\n\t\t\t\tthrow new DiscoveryError(\n\t\t\t\t\t\"discovery_not_found\",\n\t\t\t\t\t\"Discovery endpoint not found\",\n\t\t\t\t\t{\n\t\t\t\t\t\turl,\n\t\t\t\t\t\tstatus,\n\t\t\t\t\t},\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tif (status === 408) {\n\t\t\t\tthrow new DiscoveryError(\n\t\t\t\t\t\"discovery_timeout\",\n\t\t\t\t\t\"Discovery request timed out\",\n\t\t\t\t\t{\n\t\t\t\t\t\turl,\n\t\t\t\t\t\ttimeout,\n\t\t\t\t\t},\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tthrow new DiscoveryError(\n\t\t\t\t\"discovery_unexpected_error\",\n\t\t\t\t`Unexpected discovery error: ${response.error.statusText}`,\n\t\t\t\t{ url, ...response.error },\n\t\t\t);\n\t\t}\n\n\t\tif (!response.data) {\n\t\t\tthrow new DiscoveryError(\n\t\t\t\t\"discovery_invalid_json\",\n\t\t\t\t\"Discovery endpoint returned an empty response\",\n\t\t\t\t{ url },\n\t\t\t);\n\t\t}\n\n\t\tconst data = response.data as OIDCDiscoveryDocument | string;\n\t\tif (typeof data === \"string\") {\n\t\t\tthrow new DiscoveryError(\n\t\t\t\t\"discovery_invalid_json\",\n\t\t\t\t\"Discovery endpoint returned invalid JSON\",\n\t\t\t\t{ url, bodyPreview: data.slice(0, 200) },\n\t\t\t);\n\t\t}\n\n\t\treturn data;\n\t} catch (error) {\n\t\tif (error instanceof DiscoveryError) {\n\t\t\tthrow error;\n\t\t}\n\n\t\t// betterFetch throws AbortError on timeout (not returned as response.error)\n\t\t// Check error.name since message varies by runtime\n\t\tif (error instanceof Error && error.name === \"AbortError\") {\n\t\t\tthrow new DiscoveryError(\n\t\t\t\t\"discovery_timeout\",\n\t\t\t\t\"Discovery request timed out\",\n\t\t\t\t{\n\t\t\t\t\turl,\n\t\t\t\t\ttimeout,\n\t\t\t\t},\n\t\t\t);\n\t\t}\n\n\t\tthrow new DiscoveryError(\n\t\t\t\"discovery_unexpected_error\",\n\t\t\t`Unexpected error during discovery: ${error instanceof Error ? error.message : String(error)}`,\n\t\t\t{ url },\n\t\t\t{ cause: error },\n\t\t);\n\t}\n}\n\n/**\n * Validate a discovery document.\n *\n * Checks:\n * 1. All required fields are present\n * 2. Issuer matches the configured issuer (case-sensitive, exact match)\n *\n * Invariant: If this function returns without throwing, the document is safe\n * to use for hydrating OIDC config (required fields present, issuer matches\n * configured value, basic structural sanity verified).\n *\n * @param doc - The discovery document to validate\n * @param configuredIssuer - The expected issuer value\n * @throws DiscoveryError if validation fails\n */\nexport function validateDiscoveryDocument(\n\tdoc: OIDCDiscoveryDocument,\n\tconfiguredIssuer: string,\n): void {\n\tconst missingFields: string[] = [];\n\n\tfor (const field of REQUIRED_DISCOVERY_FIELDS) {\n\t\tif (!doc[field]) {\n\t\t\tmissingFields.push(field);\n\t\t}\n\t}\n\n\tif (missingFields.length > 0) {\n\t\tthrow new DiscoveryError(\n\t\t\t\"discovery_incomplete\",\n\t\t\t`Discovery document is missing required fields: ${missingFields.join(\", \")}`,\n\t\t\t{ missingFields },\n\t\t);\n\t}\n\n\tconst discoveredIssuer = doc.issuer.endsWith(\"/\")\n\t\t? doc.issuer.slice(0, -1)\n\t\t: doc.issuer;\n\tconst expectedIssuer = configuredIssuer.endsWith(\"/\")\n\t\t? configuredIssuer.slice(0, -1)\n\t\t: configuredIssuer;\n\n\tif (discoveredIssuer !== expectedIssuer) {\n\t\tthrow new DiscoveryError(\n\t\t\t\"issuer_mismatch\",\n\t\t\t`Discovered issuer \"${doc.issuer}\" does not match configured issuer \"${configuredIssuer}\"`,\n\t\t\t{\n\t\t\t\tdiscovered: doc.issuer,\n\t\t\t\tconfigured: configuredIssuer,\n\t\t\t},\n\t\t);\n\t}\n}\n\n/**\n * Normalize URLs in the discovery document.\n *\n * @param document - The discovery document\n * @param issuer - The base issuer URL\n * @param isTrustedOrigin - Origin verification tester function\n * @returns The normalized discovery document\n */\nexport function normalizeDiscoveryUrls(\n\tdocument: OIDCDiscoveryDocument,\n\tissuer: string,\n\tisTrustedOrigin: DiscoverOIDCConfigParams[\"isTrustedOrigin\"],\n): OIDCDiscoveryDocument {\n\tconst doc = { ...document };\n\n\tdoc.token_endpoint = normalizeAndValidateUrl(\n\t\t\"token_endpoint\",\n\t\tdoc.token_endpoint,\n\t\tissuer,\n\t\tisTrustedOrigin,\n\t);\n\tdoc.authorization_endpoint = normalizeAndValidateUrl(\n\t\t\"authorization_endpoint\",\n\t\tdoc.authorization_endpoint,\n\t\tissuer,\n\t\tisTrustedOrigin,\n\t);\n\n\tdoc.jwks_uri = normalizeAndValidateUrl(\n\t\t\"jwks_uri\",\n\t\tdoc.jwks_uri,\n\t\tissuer,\n\t\tisTrustedOrigin,\n\t);\n\n\tif (doc.userinfo_endpoint) {\n\t\tdoc.userinfo_endpoint = normalizeAndValidateUrl(\n\t\t\t\"userinfo_endpoint\",\n\t\t\tdoc.userinfo_endpoint,\n\t\t\tissuer,\n\t\t\tisTrustedOrigin,\n\t\t);\n\t}\n\n\tif (doc.revocation_endpoint) {\n\t\tdoc.revocation_endpoint = normalizeAndValidateUrl(\n\t\t\t\"revocation_endpoint\",\n\t\t\tdoc.revocation_endpoint,\n\t\t\tissuer,\n\t\t\tisTrustedOrigin,\n\t\t);\n\t}\n\n\tif (doc.end_session_endpoint) {\n\t\tdoc.end_session_endpoint = normalizeAndValidateUrl(\n\t\t\t\"end_session_endpoint\",\n\t\t\tdoc.end_session_endpoint,\n\t\t\tissuer,\n\t\t\tisTrustedOrigin,\n\t\t);\n\t}\n\n\tif (doc.introspection_endpoint) {\n\t\tdoc.introspection_endpoint = normalizeAndValidateUrl(\n\t\t\t\"introspection_endpoint\",\n\t\t\tdoc.introspection_endpoint,\n\t\t\tissuer,\n\t\t\tisTrustedOrigin,\n\t\t);\n\t}\n\n\treturn doc;\n}\n\n/**\n * Normalizes and validates a single URL endpoint\n * @param name The url name\n * @param endpoint The url to validate\n * @param issuer The issuer base url\n * @param isTrustedOrigin - Origin verification tester function\n * @returns\n */\nfunction normalizeAndValidateUrl(\n\tname: string,\n\tendpoint: string,\n\tissuer: string,\n\tisTrustedOrigin: DiscoverOIDCConfigParams[\"isTrustedOrigin\"],\n): string {\n\tconst url = normalizeUrl(name, endpoint, issuer);\n\n\tif (!isTrustedOrigin(url)) {\n\t\tthrow new DiscoveryError(\n\t\t\t\"discovery_untrusted_origin\",\n\t\t\t`The ${name} \"${url}\" is not trusted by your trusted origins configuration.`,\n\t\t\t{ endpoint: name, url },\n\t\t);\n\t}\n\n\treturn url;\n}\n\n/**\n * Normalize a single URL endpoint.\n *\n * @param name - The endpoint name (e.g token_endpoint)\n * @param endpoint - The endpoint URL to normalize\n * @param issuer - The base issuer URL\n * @returns The normalized endpoint URL\n */\nexport function normalizeUrl(\n\tname: string,\n\tendpoint: string,\n\tissuer: string,\n): string {\n\ttry {\n\t\treturn parseURL(name, endpoint).toString();\n\t} catch {\n\t\t// In case of error, endpoint maybe a relative url\n\t\t// So we try to resolve it relative to the issuer\n\n\t\tconst issuerURL = parseURL(name, issuer);\n\t\tconst basePath = issuerURL.pathname.replace(/\\/+$/, \"\");\n\t\tconst endpointPath = endpoint.replace(/^\\/+/, \"\");\n\n\t\treturn parseURL(\n\t\t\tname,\n\t\t\tbasePath + \"/\" + endpointPath,\n\t\t\tissuerURL.origin,\n\t\t).toString();\n\t}\n}\n\n/**\n * Parses the given URL or throws in case of invalid or unsupported protocols\n *\n * @param name the url name\n * @param endpoint the endpoint url\n * @param [base] optional base path\n * @returns\n */\nfunction parseURL(name: string, endpoint: string, base?: string) {\n\tlet endpointURL: URL | undefined;\n\n\ttry {\n\t\tendpointURL = new URL(endpoint, base);\n\t\tif (endpointURL.protocol === \"http:\" || endpointURL.protocol === \"https:\") {\n\t\t\treturn endpointURL;\n\t\t}\n\t} catch (error) {\n\t\tthrow new DiscoveryError(\n\t\t\t\"discovery_invalid_url\",\n\t\t\t`The url \"${name}\" must be valid: ${endpoint}`,\n\t\t\t{\n\t\t\t\turl: endpoint,\n\t\t\t},\n\t\t\t{ cause: error },\n\t\t);\n\t}\n\n\tthrow new DiscoveryError(\n\t\t\"discovery_invalid_url\",\n\t\t`The url \"${name}\" must use the http or https supported protocols: ${endpoint}`,\n\t\t{ url: endpoint, protocol: endpointURL.protocol },\n\t);\n}\n\n/**\n * Select the token endpoint authentication method.\n *\n * @param doc - The discovery document\n * @param existing - Existing authentication method from config\n * @returns The selected authentication method\n */\nexport function selectTokenEndpointAuthMethod(\n\tdoc: OIDCDiscoveryDocument,\n\texisting?: \"client_secret_basic\" | \"client_secret_post\",\n): \"client_secret_basic\" | \"client_secret_post\" {\n\tif (existing) {\n\t\treturn existing;\n\t}\n\n\tconst supported = doc.token_endpoint_auth_methods_supported;\n\n\tif (!supported || supported.length === 0) {\n\t\treturn \"client_secret_basic\";\n\t}\n\n\tif (supported.includes(\"client_secret_basic\")) {\n\t\treturn \"client_secret_basic\";\n\t}\n\n\tif (supported.includes(\"client_secret_post\")) {\n\t\treturn \"client_secret_post\";\n\t}\n\n\treturn \"client_secret_basic\";\n}\n\n/**\n * Check if a provider configuration needs runtime discovery.\n *\n * Returns true if we need discovery at runtime to complete the token exchange\n * and validation. Specifically checks for:\n * - `tokenEndpoint` - required for exchanging authorization code for tokens\n * - `jwksEndpoint` - required for validating ID token signatures\n *\n * Note: `authorizationEndpoint` is handled separately in the sign-in flow,\n * so it's not checked here.\n *\n * @param config - Partial OIDC config from the provider\n * @returns true if runtime discovery should be performed\n */\nexport function needsRuntimeDiscovery(\n\tconfig: Partial<HydratedOIDCConfig> | undefined,\n): boolean {\n\tif (!config) {\n\t\treturn true;\n\t}\n\n\treturn !config.tokenEndpoint || !config.jwksEndpoint;\n}\n","/**\n * OIDC Discovery Error Mapping\n *\n * Maps DiscoveryError codes to appropriate APIError responses.\n * Used at the boundary between the discovery pipeline and HTTP handlers.\n */\n\nimport { APIError } from \"better-auth/api\";\nimport type { DiscoveryError } from \"./types\";\n\n/**\n * Maps a DiscoveryError to an appropriate APIError for HTTP responses.\n *\n * Error code mapping:\n * - discovery_invalid_url → 400 BAD_REQUEST\n * - discovery_not_found → 400 BAD_REQUEST\n * - discovery_invalid_json → 400 BAD_REQUEST\n * - discovery_incomplete → 400 BAD_REQUEST\n * - issuer_mismatch → 400 BAD_REQUEST\n * - unsupported_token_auth_method → 400 BAD_REQUEST\n * - discovery_timeout → 502 BAD_GATEWAY\n * - discovery_unexpected_error → 502 BAD_GATEWAY\n *\n * @param error - The DiscoveryError to map\n * @returns An APIError with appropriate status and message\n */\nexport function mapDiscoveryErrorToAPIError(error: DiscoveryError): APIError {\n\tswitch (error.code) {\n\t\tcase \"discovery_timeout\":\n\t\t\treturn new APIError(\"BAD_GATEWAY\", {\n\t\t\t\tmessage: `OIDC discovery timed out: ${error.message}`,\n\t\t\t\tcode: error.code,\n\t\t\t});\n\n\t\tcase \"discovery_unexpected_error\":\n\t\t\treturn new APIError(\"BAD_GATEWAY\", {\n\t\t\t\tmessage: `OIDC discovery failed: ${error.message}`,\n\t\t\t\tcode: error.code,\n\t\t\t});\n\n\t\tcase \"discovery_not_found\":\n\t\t\treturn new APIError(\"BAD_REQUEST\", {\n\t\t\t\tmessage: `OIDC discovery endpoint not found. The issuer may not support OIDC discovery, or the URL is incorrect. ${error.message}`,\n\t\t\t\tcode: error.code,\n\t\t\t});\n\n\t\tcase \"discovery_invalid_url\":\n\t\t\treturn new APIError(\"BAD_REQUEST\", {\n\t\t\t\tmessage: `Invalid OIDC discovery URL: ${error.message}`,\n\t\t\t\tcode: error.code,\n\t\t\t});\n\n\t\tcase \"discovery_untrusted_origin\":\n\t\t\treturn new APIError(\"BAD_REQUEST\", {\n\t\t\t\tmessage: `Untrusted OIDC discovery URL: ${error.message}`,\n\t\t\t\tcode: error.code,\n\t\t\t});\n\n\t\tcase \"discovery_invalid_json\":\n\t\t\treturn new APIError(\"BAD_REQUEST\", {\n\t\t\t\tmessage: `OIDC discovery returned invalid data: ${error.message}`,\n\t\t\t\tcode: error.code,\n\t\t\t});\n\n\t\tcase \"discovery_incomplete\":\n\t\t\treturn new APIError(\"BAD_REQUEST\", {\n\t\t\t\tmessage: `OIDC discovery document is missing required fields: ${error.message}`,\n\t\t\t\tcode: error.code,\n\t\t\t});\n\n\t\tcase \"issuer_mismatch\":\n\t\t\treturn new APIError(\"BAD_REQUEST\", {\n\t\t\t\tmessage: `OIDC issuer mismatch: ${error.message}`,\n\t\t\t\tcode: error.code,\n\t\t\t});\n\n\t\tcase \"unsupported_token_auth_method\":\n\t\t\treturn new APIError(\"BAD_REQUEST\", {\n\t\t\t\tmessage: `Incompatible OIDC provider: ${error.message}`,\n\t\t\t\tcode: error.code,\n\t\t\t});\n\n\t\tdefault: {\n\t\t\t// Exhaustive check - TypeScript will error if we miss a case\n\t\t\tconst _exhaustiveCheck: never = error.code;\n\t\t\treturn new APIError(\"INTERNAL_SERVER_ERROR\", {\n\t\t\t\tmessage: `Unexpected discovery error: ${error.message}`,\n\t\t\t\tcode: \"discovery_unexpected_error\",\n\t\t\t});\n\t\t}\n\t}\n}\n","import type { GenericEndpointContext, StateData } from \"better-auth\";\nimport { generateGenericState, parseGenericState } from \"better-auth\";\nimport { generateRandomString } from \"better-auth/crypto\";\nimport { APIError } from \"better-call\";\n\nexport async function generateRelayState(\n\tc: GenericEndpointContext,\n\tlink:\n\t\t| {\n\t\t\t\temail: string;\n\t\t\t\tuserId: string;\n\t\t }\n\t\t| undefined,\n\tadditionalData: Record<string, any> | false | undefined,\n) {\n\tconst callbackURL = c.body.callbackURL;\n\tif (!callbackURL) {\n\t\tthrow new APIError(\"BAD_REQUEST\", {\n\t\t\tmessage: \"callbackURL is required\",\n\t\t});\n\t}\n\n\tconst codeVerifier = generateRandomString(128);\n\tconst stateData: StateData = {\n\t\t...(additionalData ? additionalData : {}),\n\t\tcallbackURL,\n\t\tcodeVerifier,\n\t\terrorURL: c.body.errorCallbackURL,\n\t\tnewUserURL: c.body.newUserCallbackURL,\n\t\tlink,\n\t\t/**\n\t\t * This is the actual expiry time of the state\n\t\t */\n\t\texpiresAt: Date.now() + 10 * 60 * 1000,\n\t\trequestSignUp: c.body.requestSignUp,\n\t};\n\n\ttry {\n\t\treturn generateGenericState(c, stateData, {\n\t\t\tcookieName: \"relay_state\",\n\t\t});\n\t} catch (error) {\n\t\tc.context.logger.error(\n\t\t\t\"Failed to create verification for relay state\",\n\t\t\terror,\n\t\t);\n\t\tthrow new APIError(\"INTERNAL_SERVER_ERROR\", {\n\t\t\tmessage: \"State error: Unable to create verification for relay state\",\n\t\t\tcause: error,\n\t\t});\n\t}\n}\n\nexport async function parseRelayState(c: GenericEndpointContext) {\n\tconst state = c.body.RelayState;\n\tconst errorURL =\n\t\tc.context.options.onAPIError?.errorURL || `${c.context.baseURL}/error`;\n\n\tlet parsedData: StateData;\n\n\ttry {\n\t\tparsedData = await parseGenericState(c, state, {\n\t\t\tcookieName: \"relay_state\",\n\t\t});\n\t} catch (error) {\n\t\tc.context.logger.error(\"Failed to parse relay state\", error);\n\t\tthrow new APIError(\"BAD_REQUEST\", {\n\t\t\tmessage: \"State error: failed to validate relay state\",\n\t\t\tcause: error,\n\t\t});\n\t}\n\n\tif (!parsedData.errorURL) {\n\t\tparsedData.errorURL = errorURL;\n\t}\n\n\treturn parsedData;\n}\n","import { BetterFetchError, betterFetch } from \"@better-fetch/fetch\";\nimport type { User, Verification } from \"better-auth\";\nimport {\n\tcreateAuthorizationURL,\n\tgenerateState,\n\tHIDE_METADATA,\n\tparseState,\n\tvalidateAuthorizationCode,\n\tvalidateToken,\n} from \"better-auth\";\nimport {\n\tAPIError,\n\tcreateAuthEndpoint,\n\tgetSessionFromCtx,\n\tsessionMiddleware,\n} from \"better-auth/api\";\nimport { setSessionCookie } from \"better-auth/cookies\";\nimport { generateRandomString } from \"better-auth/crypto\";\nimport { handleOAuthUserInfo } from \"better-auth/oauth2\";\nimport { XMLParser } from \"fast-xml-parser\";\nimport { decodeJwt } from \"jose\";\nimport * as saml from \"samlify\";\nimport type { BindingContext } from \"samlify/types/src/entity\";\nimport type { IdentityProvider } from \"samlify/types/src/entity-idp\";\nimport type { FlowResult } from \"samlify/types/src/flow\";\nimport z from \"zod/v4\";\n\ninterface AuthnRequestRecord {\n\tid: string;\n\tproviderId: string;\n\tcreatedAt: number;\n\texpiresAt: number;\n}\n\nimport {\n\tAUTHN_REQUEST_KEY_PREFIX,\n\tDEFAULT_ASSERTION_TTL_MS,\n\tDEFAULT_AUTHN_REQUEST_TTL_MS,\n\tDEFAULT_CLOCK_SKEW_MS,\n\tDEFAULT_MAX_SAML_METADATA_SIZE,\n\tDEFAULT_MAX_SAML_RESPONSE_SIZE,\n\tUSED_ASSERTION_KEY_PREFIX,\n} from \"../constants\";\nimport { assignOrganizationFromProvider } from \"../linking\";\nimport type { HydratedOIDCConfig } from \"../oidc\";\nimport {\n\tDiscoveryError,\n\tdiscoverOIDCConfig,\n\tmapDiscoveryErrorToAPIError,\n} from \"../oidc\";\nimport {\n\tvalidateConfigAlgorithms,\n\tvalidateSAMLAlgorithms,\n\tvalidateSingleAssertion,\n} from \"../saml\";\nimport { generateRelayState, parseRelayState } from \"../saml-state\";\nimport type { OIDCConfig, SAMLConfig, SSOOptions, SSOProvider } from \"../types\";\nimport { domainMatches, safeJsonParse, validateEmailDomain } from \"../utils\";\n\nexport interface TimestampValidationOptions {\n\tclockSkew?: number;\n\trequireTimestamps?: boolean;\n\tlogger?: {\n\t\twarn: (message: string, data?: Record<string, unknown>) => void;\n\t};\n}\n\n/** Conditions extracted from SAML assertion */\nexport interface SAMLConditions {\n\tnotBefore?: string;\n\tnotOnOrAfter?: string;\n}\n\n/**\n * Validates SAML assertion timestamp conditions (NotBefore/NotOnOrAfter).\n * Prevents acceptance of expired or future-dated assertions.\n * @throws {APIError} If timestamps are invalid, expired, or not yet valid\n */\nexport function validateSAMLTimestamp(\n\tconditions: SAMLConditions | undefined,\n\toptions: TimestampValidationOptions = {},\n): void {\n\tconst clockSkew = options.clockSkew ?? DEFAULT_CLOCK_SKEW_MS;\n\tconst hasTimestamps = conditions?.notBefore || conditions?.notOnOrAfter;\n\n\tif (!hasTimestamps) {\n\t\tif (options.requireTimestamps) {\n\t\t\tthrow new APIError(\"BAD_REQUEST\", {\n\t\t\t\tmessage: \"SAML assertion missing required timestamp conditions\",\n\t\t\t\tdetails:\n\t\t\t\t\t\"Assertions must include NotBefore and/or NotOnOrAfter conditions\",\n\t\t\t});\n\t\t}\n\t\t// Log warning for missing timestamps when not required\n\t\toptions.logger?.warn(\n\t\t\t\"SAML assertion accepted without timestamp conditions\",\n\t\t\t{ hasConditions: !!conditions },\n\t\t);\n\t\treturn;\n\t}\n\n\tconst now = Date.now();\n\n\tif (conditions?.notBefore) {\n\t\tconst notBeforeTime = new Date(conditions.notBefore).getTime();\n\t\tif (Number.isNaN(notBeforeTime)) {\n\t\t\tthrow new APIError(\"BAD_REQUEST\", {\n\t\t\t\tmessage: \"SAML assertion has invalid NotBefore timestamp\",\n\t\t\t\tdetails: `Unable to parse NotBefore value: ${conditions.notBefore}`,\n\t\t\t});\n\t\t}\n\t\tif (now < notBeforeTime - clockSkew) {\n\t\t\tthrow new APIError(\"BAD_REQUEST\", {\n\t\t\t\tmessage: \"SAML assertion is not yet valid\",\n\t\t\t\tdetails: `Current time is before NotBefore (with ${clockSkew}ms clock skew tolerance)`,\n\t\t\t});\n\t\t}\n\t}\n\n\tif (conditions?.notOnOrAfter) {\n\t\tconst notOnOrAfterTime = new Date(conditions.notOnOrAfter).getTime();\n\t\tif (Number.isNaN(notOnOrAfterTime)) {\n\t\t\tthrow new APIError(\"BAD_REQUEST\", {\n\t\t\t\tmessage: \"SAML assertion has invalid NotOnOrAfter timestamp\",\n\t\t\t\tdetails: `Unable to parse NotOnOrAfter value: ${conditions.notOnOrAfter}`,\n\t\t\t});\n\t\t}\n\t\tif (now > notOnOrAfterTime + clockSkew) {\n\t\t\tthrow new APIError(\"BAD_REQUEST\", {\n\t\t\t\tmessage: \"SAML assertion has expired\",\n\t\t\t\tdetails: `Current time is after NotOnOrAfter (with ${clockSkew}ms clock skew tolerance)`,\n\t\t\t});\n\t\t}\n\t}\n}\n\n/**\n * Extracts the Assertion ID from a SAML response XML.\n * Returns null if the assertion ID cannot be found.\n */\nfunction extractAssertionId(samlContent: string): string | null {\n\ttry {\n\t\tconst parser = new XMLParser({\n\t\t\tignoreAttributes: false,\n\t\t\tattributeNamePrefix: \"@_\",\n\t\t\tremoveNSPrefix: true,\n\t\t});\n\t\tconst parsed = parser.parse(samlContent);\n\n\t\tconst response = parsed.Response || parsed[\"samlp:Response\"];\n\t\tif (!response) return null;\n\n\t\tconst rawAssertion = response.Assertion || response[\"saml:Assertion\"];\n\t\tconst assertion = Array.isArray(rawAssertion)\n\t\t\t? rawAssertion[0]\n\t\t\t: rawAssertion;\n\t\tif (!assertion) return null;\n\n\t\treturn assertion[\"@_ID\"] || null;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nconst spMetadataQuerySchema = z.object({\n\tproviderId: z.string(),\n\tformat: z.enum([\"xml\", \"json\"]).default(\"xml\"),\n});\n\ntype RelayState = Awaited<ReturnType<typeof parseRelayState>>;\n\nexport const spMetadata = () => {\n\treturn createAuthEndpoint(\n\t\t\"/sso/saml2/sp/metadata\",\n\t\t{\n\t\t\tmethod: \"GET\",\n\t\t\tquery: spMetadataQuerySchema,\n\t\t\tmetadata: {\n\t\t\t\topenapi: {\n\t\t\t\t\toperationId: \"getSSOServiceProviderMetadata\",\n\t\t\t\t\tsummary: \"Get Service Provider metadata\",\n\t\t\t\t\tdescription: \"Returns the SAML metadata for the Service Provider\",\n\t\t\t\t\tresponses: {\n\t\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\tdescription: \"SAML metadata in XML format\",\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\tasync (ctx) => {\n\t\t\tconst provider = await ctx.context.adapter.findOne<{\n\t\t\t\tid: string;\n\t\t\t\tsamlConfig: string;\n\t\t\t}>({\n\t\t\t\tmodel: \"ssoProvider\",\n\t\t\t\twhere: [\n\t\t\t\t\t{\n\t\t\t\t\t\tfield: \"providerId\",\n\t\t\t\t\t\tvalue: ctx.query.providerId,\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t});\n\t\t\tif (!provider) {\n\t\t\t\tthrow new APIError(\"NOT_FOUND\", {\n\t\t\t\t\tmessage: \"No provider found for the given providerId\",\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tconst parsedSamlConfig = safeJsonParse<SAMLConfig>(provider.samlConfig);\n\t\t\tif (!parsedSamlConfig) {\n\t\t\t\tthrow new APIError(\"BAD_REQUEST\", {\n\t\t\t\t\tmessage: \"Invalid SAML configuration\",\n\t\t\t\t});\n\t\t\t}\n\t\t\tconst sp = parsedSamlConfig.spMetadata.metadata\n\t\t\t\t? saml.ServiceProvider({\n\t\t\t\t\t\tmetadata: parsedSamlConfig.spMetadata.metadata,\n\t\t\t\t\t})\n\t\t\t\t: saml.SPMetadata({\n\t\t\t\t\t\tentityID:\n\t\t\t\t\t\t\tparsedSamlConfig.spMetadata?.entityID || parsedSamlConfig.issuer,\n\t\t\t\t\t\tassertionConsumerService: [\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tBinding: \"urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST\",\n\t\t\t\t\t\t\t\tLocation:\n\t\t\t\t\t\t\t\t\tparsedSamlConfig.callbackUrl ||\n\t\t\t\t\t\t\t\t\t`${ctx.context.baseURL}/sso/saml2/sp/acs/${provider.id}`,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t],\n\t\t\t\t\t\twantMessageSigned: parsedSamlConfig.wantAssertionsSigned || false,\n\t\t\t\t\t\tauthnRequestsSigned: parsedSamlConfig.authnRequestsSigned || false,\n\t\t\t\t\t\tnameIDFormat: parsedSamlConfig.identifierFormat\n\t\t\t\t\t\t\t? [parsedSamlConfig.identifierFormat]\n\t\t\t\t\t\t\t: undefined,\n\t\t\t\t\t});\n\t\t\treturn new Response(sp.getMetadata(), {\n\t\t\t\theaders: {\n\t\t\t\t\t\"Content-Type\": \"application/xml\",\n\t\t\t\t},\n\t\t\t});\n\t\t},\n\t);\n};\n\nconst ssoProviderBodySchema = z.object({\n\tproviderId: z.string({}).meta({\n\t\tdescription:\n\t\t\t\"The ID of the provider. This is used to identify the provider during login and callback\",\n\t}),\n\tissuer: z.string({}).meta({\n\t\tdescription: \"The issuer of the provider\",\n\t}),\n\tdomain: z.string({}).meta({\n\t\tdescription:\n\t\t\t\"The domain(s) of the provider. For enterprise multi-domain SSO where a single IdP serves multiple email domains, use comma-separated values (e.g., 'company.com,subsidiary.com,acquired-company.com')\",\n\t}),\n\toidcConfig: z\n\t\t.object({\n\t\t\tclientId: z.string({}).meta({\n\t\t\t\tdescription: \"The client ID\",\n\t\t\t}),\n\t\t\tclientSecret: z.string({}).meta({\n\t\t\t\tdescription: \"The client secret\",\n\t\t\t}),\n\t\t\tauthorizationEndpoint: z\n\t\t\t\t.string({})\n\t\t\t\t.meta({\n\t\t\t\t\tdescription: \"The authorization endpoint\",\n\t\t\t\t})\n\t\t\t\t.optional(),\n\t\t\ttokenEndpoint: z\n\t\t\t\t.string({})\n\t\t\t\t.meta({\n\t\t\t\t\tdescription: \"The token endpoint\",\n\t\t\t\t})\n\t\t\t\t.optional(),\n\t\t\tuserInfoEndpoint: z\n\t\t\t\t.string({})\n\t\t\t\t.meta({\n\t\t\t\t\tdescription: \"The user info endpoint\",\n\t\t\t\t})\n\t\t\t\t.optional(),\n\t\t\ttokenEndpointAuthentication: z\n\t\t\t\t.enum([\"client_secret_post\", \"client_secret_basic\"])\n\t\t\t\t.optional(),\n\t\t\tjwksEndpoint: z\n\t\t\t\t.string({})\n\t\t\t\t.meta({\n\t\t\t\t\tdescription: \"The JWKS endpoint\",\n\t\t\t\t})\n\t\t\t\t.optional(),\n\t\t\tdiscoveryEndpoint: z.string().optional(),\n\t\t\tskipDiscovery: z\n\t\t\t\t.boolean()\n\t\t\t\t.meta({\n\t\t\t\t\tdescription:\n\t\t\t\t\t\t\"Skip OIDC discovery during registration. When true, you must provide authorizationEndpoint, tokenEndpoint, and jwksEndpoint manually.\",\n\t\t\t\t})\n\t\t\t\t.optional(),\n\t\t\tscopes: z\n\t\t\t\t.array(z.string(), {})\n\t\t\t\t.meta({\n\t\t\t\t\tdescription:\n\t\t\t\t\t\t\"The scopes to request. Defaults to ['openid', 'email', 'profile', 'offline_access']\",\n\t\t\t\t})\n\t\t\t\t.optional(),\n\t\t\tpkce: z\n\t\t\t\t.boolean({})\n\t\t\t\t.meta({\n\t\t\t\t\tdescription: \"Whether to use PKCE for the authorization flow\",\n\t\t\t\t})\n\t\t\t\t.default(true)\n\t\t\t\t.optional(),\n\t\t\tmapping: z\n\t\t\t\t.object({\n\t\t\t\t\tid: z.string({}).meta({\n\t\t\t\t\t\tdescription: \"Field mapping for user ID (defaults to 'sub')\",\n\t\t\t\t\t}),\n\t\t\t\t\temail: z.string({}).meta({\n\t\t\t\t\t\tdescription: \"Field mapping for email (defaults to 'email')\",\n\t\t\t\t\t}),\n\t\t\t\t\temailVerified: z\n\t\t\t\t\t\t.string({})\n\t\t\t\t\t\t.meta({\n\t\t\t\t\t\t\tdescription:\n\t\t\t\t\t\t\t\t\"Field mapping for email verification (defaults to 'email_verified')\",\n\t\t\t\t\t\t})\n\t\t\t\t\t\t.optional(),\n\t\t\t\t\tname: z.string({}).meta({\n\t\t\t\t\t\tdescription: \"Field mapping for name (defaults to 'name')\",\n\t\t\t\t\t}),\n\t\t\t\t\timage: z\n\t\t\t\t\t\t.string({})\n\t\t\t\t\t\t.meta({\n\t\t\t\t\t\t\tdescription: \"Field mapping for image (defaults to 'picture')\",\n\t\t\t\t\t\t})\n\t\t\t\t\t\t.optional(),\n\t\t\t\t\textraFields: z.record(z.string(), z.any()).optional(),\n\t\t\t\t})\n\t\t\t\t.optional(),\n\t\t})\n\t\t.optional(),\n\tsamlConfig: z\n\t\t.object({\n\t\t\tentryPoint: z.string({}).meta({\n\t\t\t\tdescription: \"The entry point of the provider\",\n\t\t\t}),\n\t\t\tcert: z.string({}).meta({\n\t\t\t\tdescription: \"The certificate of the provider\",\n\t\t\t}),\n\t\t\tcallbackUrl: z.string({}).meta({\n\t\t\t\tdescription: \"The callback URL of the provider\",\n\t\t\t}),\n\t\t\taudience: z.string().optional(),\n\t\t\tidpMetadata: z\n\t\t\t\t.object({\n\t\t\t\t\tmetadata: z.string().optional(),\n\t\t\t\t\tentityID: z.string().optional(),\n\t\t\t\t\tcert: z.string().optional(),\n\t\t\t\t\tprivateKey: z.string().optional(),\n\t\t\t\t\tprivateKeyPass: z.string().optional(),\n\t\t\t\t\tisAssertionEncrypted: z.boolean().optional(),\n\t\t\t\t\tencPrivateKey: z.string().optional(),\n\t\t\t\t\tencPrivateKeyPass: z.string().optional(),\n\t\t\t\t\tsingleSignOnService: z\n\t\t\t\t\t\t.array(\n\t\t\t\t\t\t\tz.object({\n\t\t\t\t\t\t\t\tBinding: z.string().meta({\n\t\t\t\t\t\t\t\t\tdescription: \"The binding type for the SSO service\",\n\t\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t\tLocation: z.string().meta({\n\t\t\t\t\t\t\t\t\tdescription: \"The URL for the SSO service\",\n\t\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t)\n\t\t\t\t\t\t.optional()\n\t\t\t\t\t\t.meta({\n\t\t\t\t\t\t\tdescription: \"Single Sign-On service configuration\",\n\t\t\t\t\t\t}),\n\t\t\t\t})\n\t\t\t\t.optional(),\n\t\t\tspMetadata: z.object({\n\t\t\t\tmetadata: z.string().optional(),\n\t\t\t\tentityID: z.string().optional(),\n\t\t\t\tbinding: z.string().optional(),\n\t\t\t\tprivateKey: z.string().optional(),\n\t\t\t\tprivateKeyPass: z.string().optional(),\n\t\t\t\tisAssertionEncrypted: z.boolean().optional(),\n\t\t\t\tencPrivateKey: z.string().optional(),\n\t\t\t\tencPrivateKeyPass: z.string().optional(),\n\t\t\t}),\n\t\t\twantAssertionsSigned: z.boolean().optional(),\n\t\t\tauthnRequestsSigned: z.boolean().optional(),\n\t\t\tsignatureAlgorithm: z.string().optional(),\n\t\t\tdigestAlgorithm: z.string().optional(),\n\t\t\tidentifierFormat: z.string().optional(),\n\t\t\tprivateKey: z.string().optional(),\n\t\t\tdecryptionPvk: z.string().optional(),\n\t\t\tadditionalParams: z.record(z.string(), z.any()).optional(),\n\t\t\tmapping: z\n\t\t\t\t.object({\n\t\t\t\t\tid: z.string({}).meta({\n\t\t\t\t\t\tdescription: \"Field mapping for user ID (defaults to 'nameID')\",\n\t\t\t\t\t}),\n\t\t\t\t\temail: z.string({}).meta({\n\t\t\t\t\t\tdescription: \"Field mapping for email (defaults to 'email')\",\n\t\t\t\t\t}),\n\t\t\t\t\temailVerified: z\n\t\t\t\t\t\t.string({})\n\t\t\t\t\t\t.meta({\n\t\t\t\t\t\t\tdescription: \"Field mapping for email verification\",\n\t\t\t\t\t\t})\n\t\t\t\t\t\t.optional(),\n\t\t\t\t\tname: z.string({}).meta({\n\t\t\t\t\t\tdescription: \"Field mapping for name (defaults to 'displayName')\",\n\t\t\t\t\t}),\n\t\t\t\t\tfirstName: z\n\t\t\t\t\t\t.string({})\n\t\t\t\t\t\t.meta({\n\t\t\t\t\t\t\tdescription:\n\t\t\t\t\t\t\t\t\"Field mapping for first name (defaults to 'givenName')\",\n\t\t\t\t\t\t})\n\t\t\t\t\t\t.optional(),\n\t\t\t\t\tlastName: z\n\t\t\t\t\t\t.string({})\n\t\t\t\t\t\t.meta({\n\t\t\t\t\t\t\tdescription:\n\t\t\t\t\t\t\t\t\"Field mapping for last name (defaults to 'surname')\",\n\t\t\t\t\t\t})\n\t\t\t\t\t\t.optional(),\n\t\t\t\t\textraFields: z.record(z.string(), z.any()).optional(),\n\t\t\t\t})\n\t\t\t\t.optional(),\n\t\t})\n\t\t.optional(),\n\torganizationId: z\n\t\t.string({})\n\t\t.meta({\n\t\t\tdescription:\n\t\t\t\t\"If organization plugin is enabled, the organization id to link the provider to\",\n\t\t})\n\t\t.optional(),\n\toverrideUserInfo: z\n\t\t.boolean({})\n\t\t.meta({\n\t\t\tdescription:\n\t\t\t\t\"Override user info with the provider info. Defaults to false\",\n\t\t})\n\t\t.default(false)\n\t\t.optional(),\n});\n\nexport const registerSSOProvider = <O extends SSOOptions>(options: O) => {\n\treturn createAuthEndpoint(\n\t\t\"/sso/register\",\n\t\t{\n\t\t\tmethod: \"POST\",\n\t\t\tbody: ssoProviderBodySchema,\n\t\t\tuse: [sessionMiddleware],\n\t\t\tmetadata: {\n\t\t\t\topenapi: {\n\t\t\t\t\toperationId: \"registerSSOProvider\",\n\t\t\t\t\tsummary: \"Register an OIDC provider\",\n\t\t\t\t\tdescription:\n\t\t\t\t\t\t\"This endpoint is used to register an OIDC provider. This is used to configure the provider and link it to an organization\",\n\t\t\t\t\tresponses: {\n\t\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\tdescription: \"OIDC provider created successfully\",\n\t\t\t\t\t\t\tcontent: {\n\t\t\t\t\t\t\t\t\"application/json\": {\n\t\t\t\t\t\t\t\t\tschema: {\n\t\t\t\t\t\t\t\t\t\ttype: \"object\",\n\t\t\t\t\t\t\t\t\t\tproperties: {\n\t\t\t\t\t\t\t\t\t\t\tissuer: {\n\t\t\t\t\t\t\t\t\t\t\t\ttype: \"string\",\n\t\t\t\t\t\t\t\t\t\t\t\tformat: \"uri\",\n\t\t\t\t\t\t\t\t\t\t\t\tdescription: \"The issuer URL of the provider\",\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\tdomain: {\n\t\t\t\t\t\t\t\t\t\t\t\ttype: \"string\",\n\t\t\t\t\t\t\t\t\t\t\t\tdescription:\n\t\t\t\t\t\t\t\t\t\t\t\t\t\"The domain of the provider, used for email matching\",\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\tdomainVerified: {\n\t\t\t\t\t\t\t\t\t\t\t\ttype: \"boolean\",\n\t\t\t\t\t\t\t\t\t\t\t\tdescription:\n\t\t\t\t\t\t\t\t\t\t\t\t\t\"A boolean indicating whether the domain has been verified or not\",\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\tdomainVerificationToken: {\n\t\t\t\t\t\t\t\t\t\t\t\ttype: \"string\",\n\t\t\t\t\t\t\t\t\t\t\t\tdescription:\n\t\t\t\t\t\t\t\t\t\t\t\t\t\"Domain verification token. It can be used to prove ownership over the SSO domain\",\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\toidcConfig: {\n\t\t\t\t\t\t\t\t\t\t\t\ttype: \"object\",\n\t\t\t\t\t\t\t\t\t\t\t\tproperties: {\n\t\t\t\t\t\t\t\t\t\t\t\t\tissuer: {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\ttype: \"string\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tformat: \"uri\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tdescription: \"The issuer URL of the provider\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\tpkce: {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\ttype: \"boolean\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tdescription:\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"Whether PKCE is enabled for the authorization flow\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\tclientId: {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\ttype: \"string\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tdescription: \"The client ID for the provider\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\tclientSecret: {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\ttype: \"string\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tdescription: \"The client secret for the provider\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\tauthorizationEndpoint: {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\ttype: \"string\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tformat: \"uri\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tnullable: true,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tdescription: \"The authorization endpoint URL\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\tdiscoveryEndpoint: {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\ttype: \"string\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tformat: \"uri\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tdescription: \"The discovery endpoint URL\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\tuserInfoEndpoint: {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\ttype: \"string\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tformat: \"uri\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tnullable: true,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tdescription: \"The user info endpoint URL\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\tscopes: {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\ttype: \"array\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\titems: { type: \"string\" },\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tnullable: true,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tdescription:\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"The scopes requested from the provider\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\ttokenEndpoint: {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\ttype: \"string\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tformat: \"uri\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tnullable: true,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tdescription: \"The token endpoint URL\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\ttokenEndpointAuthentication: {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\ttype: \"string\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tenum: [\"client_secret_post\", \"client_secret_basic\"],\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tnullable: true,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tdescription:\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"Authentication method for the token endpoint\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\tjwksEndpoint: {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\ttype: \"string\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tformat: \"uri\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tnullable: true,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tdescription: \"The JWKS endpoint URL\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\tmapping: {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\ttype: \"object\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tnullable: true,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tproperties: {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tid: {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\ttype: \"string\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tdescription:\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"Field mapping for user ID (defaults to 'sub')\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\temail: {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\ttype: \"string\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tdescription:\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"Field mapping for email (defaults to 'email')\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\temailVerified: {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\ttype: \"string\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tnullable: true,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tdescription:\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"Field mapping for email verification (defaults to 'email_verified')\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tname: {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\ttype: \"string\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tdescription:\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"Field mapping for name (defaults to 'name')\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\timage: {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\ttype: \"string\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tnullable: true,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tdescription:\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"Field mapping for image (defaults to 'picture')\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\textraFields: {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\ttype: \"object\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tadditionalProperties: { type: \"string\" },\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tnullable: true,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tdescription: \"Additional field mappings\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\trequired: [\"id\", \"email\", \"name\"],\n\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\trequired: [\n\t\t\t\t\t\t\t\t\t\t\t\t\t\"issuer\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\"pkce\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\"clientId\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\"clientSecret\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\"discoveryEndpoint\",\n\t\t\t\t\t\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t\t\t\t\t\tdescription: \"OIDC configuration for the provider\",\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\torganizationId: {\n\t\t\t\t\t\t\t\t\t\t\t\ttype: \"string\",\n\t\t\t\t\t\t\t\t\t\t\t\tnullable: true,\n\t\t\t\t\t\t\t\t\t\t\t\tdescription: \"ID of the linked organization, if any\",\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\tuserId: {\n\t\t\t\t\t\t\t\t\t\t\t\ttype: \"string\",\n\t\t\t\t\t\t\t\t\t\t\t\tdescription:\n\t\t\t\t\t\t\t\t\t\t\t\t\t\"ID of the user who registered the provider\",\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\tproviderId: {\n\t\t\t\t\t\t\t\t\t\t\t\ttype: \"string\",\n\t\t\t\t\t\t\t\t\t\t\t\tdescription: \"Unique identifier for the provider\",\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\tredirectURI: {\n\t\t\t\t\t\t\t\t\t\t\t\ttype: \"string\",\n\t\t\t\t\t\t\t\t\t\t\t\tformat: \"uri\",\n\t\t\t\t\t\t\t\t\t\t\t\tdescription:\n\t\t\t\t\t\t\t\t\t\t\t\t\t\"The redirect URI for the provider callback\",\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\trequired: [\n\t\t\t\t\t\t\t\t\t\t\t\"issuer\",\n\t\t\t\t\t\t\t\t\t\t\t\"domain\",\n\t\t\t\t\t\t\t\t\t\t\t\"oidcConfig\",\n\t\t\t\t\t\t\t\t\t\t\t\"userId\",\n\t\t\t\t\t\t\t\t\t\t\t\"providerId\",\n\t\t\t\t\t\t\t\t\t\t\t\"redirectURI\",\n\t\t\t\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\tasync (ctx) => {\n\t\t\tconst user = ctx.context.session?.user;\n\t\t\tif (!user) {\n\t\t\t\tthrow new APIError(\"UNAUTHORIZED\");\n\t\t\t}\n\n\t\t\tconst limit =\n\t\t\t\ttypeof options?.providersLimit === \"function\"\n\t\t\t\t\t? await options.providersLimit(user)\n\t\t\t\t\t: (options?.providersLimit ?? 10);\n\n\t\t\tif (!limit) {\n\t\t\t\tthrow new APIError(\"FORBIDDEN\", {\n\t\t\t\t\tmessage: \"SSO provider registration is disabled\",\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tconst providers = await ctx.context.adapter.findMany({\n\t\t\t\tmodel: \"ssoProvider\",\n\t\t\t\twhere: [{ field: \"userId\", value: user.id }],\n\t\t\t});\n\n\t\t\tif (providers.length >= limit) {\n\t\t\t\tthrow new APIError(\"FORBIDDEN\", {\n\t\t\t\t\tmessage: \"You have reached the maximum number of SSO providers\",\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tconst body = ctx.body;\n\t\t\tconst issuerValidator = z.string().url();\n\t\t\tif (issuerValidator.safeParse(body.issuer).error) {\n\t\t\t\tthrow new APIError(\"BAD_REQUEST\", {\n\t\t\t\t\tmessage: \"Invalid issuer. Must be a valid URL\",\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tif (body.samlConfig?.idpMetadata?.metadata) {\n\t\t\t\tconst maxMetadataSize =\n\t\t\t\t\toptions?.saml?.maxMetadataSize ?? DEFAULT_MAX_SAML_METADATA_SIZE;\n\t\t\t\tif (\n\t\t\t\t\tnew TextEncoder().encode(body.samlConfig.idpMetadata.metadata)\n\t\t\t\t\t\t.length > maxMetadataSize\n\t\t\t\t) {\n\t\t\t\t\tthrow new APIError(\"BAD_REQUEST\", {\n\t\t\t\t\t\tmessage: `IdP metadata exceeds maximum allowed size (${maxMetadataSize} bytes)`,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (ctx.body.organizationId) {\n\t\t\t\tconst organization = await ctx.context.adapter.findOne({\n\t\t\t\t\tmodel: \"member\",\n\t\t\t\t\twhere: [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tfield: \"userId\",\n\t\t\t\t\t\t\tvalue: user.id,\n\t\t\t\t\t\t},\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tfield: \"organizationId\",\n\t\t\t\t\t\t\tvalue: ctx.body.organizationId,\n\t\t\t\t\t\t},\n\t\t\t\t\t],\n\t\t\t\t});\n\t\t\t\tif (!organization) {\n\t\t\t\t\tthrow new APIError(\"BAD_REQUEST\", {\n\t\t\t\t\t\tmessage: \"You are not a member of the organization\",\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst existingProvider = await ctx.context.adapter.findOne({\n\t\t\t\tmodel: \"ssoProvider\",\n\t\t\t\twhere: [\n\t\t\t\t\t{\n\t\t\t\t\t\tfield: \"providerId\",\n\t\t\t\t\t\tvalue: body.providerId,\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t});\n\n\t\t\tif (existingProvider) {\n\t\t\t\tctx.context.logger.info(\n\t\t\t\t\t`SSO provider creation attempt with existing providerId: ${body.providerId}`,\n\t\t\t\t);\n\t\t\t\tthrow new APIError(\"UNPROCESSABLE_ENTITY\", {\n\t\t\t\t\tmessage: \"SSO provider with this providerId already exists\",\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tlet hydratedOIDCConfig: HydratedOIDCConfig | null = null;\n\t\t\tif (body.oidcConfig && !body.oidcConfig.skipDiscovery) {\n\t\t\t\ttry {\n\t\t\t\t\thydratedOIDCConfig = await discoverOIDCConfig({\n\t\t\t\t\t\tissuer: body.issuer,\n\t\t\t\t\t\texistingConfig: {\n\t\t\t\t\t\t\tdiscoveryEndpoint: body.oidcConfig.discoveryEndpoint,\n\t\t\t\t\t\t\tauthorizationEndpoint: body.oidcConfig.authorizationEndpoint,\n\t\t\t\t\t\t\ttokenEndpoint: body.oidcConfig.tokenEndpoint,\n\t\t\t\t\t\t\tjwksEndpoint: body.oidcConfig.jwksEndpoint,\n\t\t\t\t\t\t\tuserInfoEndpoint: body.oidcConfig.userInfoEndpoint,\n\t\t\t\t\t\t\ttokenEndpointAuthentication:\n\t\t\t\t\t\t\t\tbody.oidcConfig.tokenEndpointAuthentication,\n\t\t\t\t\t\t},\n\t\t\t\t\t\tisTrustedOrigin: (url: string) => ctx.context.isTrustedOrigin(url),\n\t\t\t\t\t});\n\t\t\t\t} catch (error) {\n\t\t\t\t\tif (error instanceof DiscoveryError) {\n\t\t\t\t\t\tthrow mapDiscoveryErrorToAPIError(error);\n\t\t\t\t\t}\n\t\t\t\t\tthrow error;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst buildOIDCConfig = () => {\n\t\t\t\tif (!body.oidcConfig) return null;\n\n\t\t\t\tif (body.oidcConfig.skipDiscovery) {\n\t\t\t\t\treturn JSON.stringify({\n\t\t\t\t\t\tissuer: body.issuer,\n\t\t\t\t\t\tclientId: body.oidcConfig.clientId,\n\t\t\t\t\t\tclientSecret: body.oidcConfig.clientSecret,\n\t\t\t\t\t\tauthorizationEndpoint: body.oidcConfig.authorizationEndpoint,\n\t\t\t\t\t\ttokenEndpoint: body.oidcConfig.tokenEndpoint,\n\t\t\t\t\t\ttokenEndpointAuthentication:\n\t\t\t\t\t\t\tbody.oidcConfig.tokenEndpointAuthentication ||\n\t\t\t\t\t\t\t\"client_secret_basic\",\n\t\t\t\t\t\tjwksEndpoint: body.oidcConfig.jwksEndpoint,\n\t\t\t\t\t\tpkce: body.oidcConfig.pkce,\n\t\t\t\t\t\tdiscoveryEndpoint:\n\t\t\t\t\t\t\tbody.oidcConfig.discoveryEndpoint ||\n\t\t\t\t\t\t\t`${body.issuer}/.well-known/openid-configuration`,\n\t\t\t\t\t\tmapping: body.oidcConfig.mapping,\n\t\t\t\t\t\tscopes: body.oidcConfig.scopes,\n\t\t\t\t\t\tuserInfoEndpoint: body.oidcConfig.userInfoEndpoint,\n\t\t\t\t\t\toverrideUserInfo:\n\t\t\t\t\t\t\tctx.body.overrideUserInfo ||\n\t\t\t\t\t\t\toptions?.defaultOverrideUserInfo ||\n\t\t\t\t\t\t\tfalse,\n\t\t\t\t\t});\n\t\t\t\t}\n\n\t\t\t\tif (!hydratedOIDCConfig) return null;\n\n\t\t\t\treturn JSON.stringify({\n\t\t\t\t\tissuer: hydratedOIDCConfig.issuer,\n\t\t\t\t\tclientId: body.oidcConfig.clientId,\n\t\t\t\t\tclientSecret: body.oidcConfig.clientSecret,\n\t\t\t\t\tauthorizationEndpoint: hydratedOIDCConfig.authorizationEndpoint,\n\t\t\t\t\ttokenEndpoint: hydratedOIDCConfig.tokenEndpoint,\n\t\t\t\t\ttokenEndpointAuthentication:\n\t\t\t\t\t\thydratedOIDCConfig.tokenEndpointAuthentication,\n\t\t\t\t\tjwksEndpoint: hydratedOIDCConfig.jwksEndpoint,\n\t\t\t\t\tpkce: body.oidcConfig.pkce,\n\t\t\t\t\tdiscoveryEndpoint: hydratedOIDCConfig.discoveryEndpoint,\n\t\t\t\t\tmapping: body.oidcConfig.mapping,\n\t\t\t\t\tscopes: body.oidcConfig.scopes,\n\t\t\t\t\tuserInfoEndpoint: hydratedOIDCConfig.userInfoEndpoint,\n\t\t\t\t\toverrideUserInfo:\n\t\t\t\t\t\tctx.body.overrideUserInfo ||\n\t\t\t\t\t\toptions?.defaultOverrideUserInfo ||\n\t\t\t\t\t\tfalse,\n\t\t\t\t});\n\t\t\t};\n\n\t\t\tif (body.samlConfig) {\n\t\t\t\tvalidateConfigAlgorithms(\n\t\t\t\t\t{\n\t\t\t\t\t\tsignatureAlgorithm: body.samlConfig.signatureAlgorithm,\n\t\t\t\t\t\tdigestAlgorithm: body.samlConfig.digestAlgorithm,\n\t\t\t\t\t},\n\t\t\t\t\toptions?.saml?.algorithms,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst provider = await ctx.context.adapter.create<\n\t\t\t\tRecord<string, any>,\n\t\t\t\tSSOProvider<O>\n\t\t\t>({\n\t\t\t\tmodel: \"ssoProvider\",\n\t\t\t\tdata: {\n\t\t\t\t\tissuer: body.issuer,\n\t\t\t\t\tdomain: body.domain,\n\t\t\t\t\tdomainVerified: false,\n\t\t\t\t\toidcConfig: buildOIDCConfig(),\n\t\t\t\t\tsamlConfig: body.samlConfig\n\t\t\t\t\t\t? JSON.stringify({\n\t\t\t\t\t\t\t\tissuer: body.issuer,\n\t\t\t\t\t\t\t\tentryPoint: body.samlConfig.entryPoint,\n\t\t\t\t\t\t\t\tcert: body.samlConfig.cert,\n\t\t\t\t\t\t\t\tcallbackUrl: body.samlConfig.callbackUrl,\n\t\t\t\t\t\t\t\taudience: body.samlConfig.audience,\n\t\t\t\t\t\t\t\tidpMetadata: body.samlConfig.idpMetadata,\n\t\t\t\t\t\t\t\tspMetadata: body.samlConfig.spMetadata,\n\t\t\t\t\t\t\t\twantAssertionsSigned: body.samlConfig.wantAssertionsSigned,\n\t\t\t\t\t\t\t\tauthnRequestsSigned: body.samlConfig.authnRequestsSigned,\n\t\t\t\t\t\t\t\tsignatureAlgorithm: body.samlConfig.signatureAlgorithm,\n\t\t\t\t\t\t\t\tdigestAlgorithm: body.samlConfig.digestAlgorithm,\n\t\t\t\t\t\t\t\tidentifierFormat: body.samlConfig.identifierFormat,\n\t\t\t\t\t\t\t\tprivateKey: body.samlConfig.privateKey,\n\t\t\t\t\t\t\t\tdecryptionPvk: body.samlConfig.decryptionPvk,\n\t\t\t\t\t\t\t\tadditionalParams: body.samlConfig.additionalParams,\n\t\t\t\t\t\t\t\tmapping: body.samlConfig.mapping,\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t: null,\n\t\t\t\t\torganizationId: body.organizationId,\n\t\t\t\t\tuserId: ctx.context.session.user.id,\n\t\t\t\t\tproviderId: body.providerId,\n\t\t\t\t},\n\t\t\t});\n\n\t\t\tlet domainVerificationToken: string | undefined;\n\t\t\tlet domainVerified: boolean | undefined;\n\n\t\t\tif (options?.domainVerification?.enabled) {\n\t\t\t\tdomainVerified = false;\n\t\t\t\tdomainVerificationToken = generateRandomString(24);\n\n\t\t\t\tawait ctx.context.adapter.create<Verification>({\n\t\t\t\t\tmodel: \"verification\",\n\t\t\t\t\tdata: {\n\t\t\t\t\t\tidentifier: options.domainVerification?.tokenPrefix\n\t\t\t\t\t\t\t? `${options.domainVerification?.tokenPrefix}-${provider.providerId}`\n\t\t\t\t\t\t\t: `better-auth-token-${provider.providerId}`,\n\t\t\t\t\t\tcreatedAt: new Date(),\n\t\t\t\t\t\tupdatedAt: new Date(),\n\t\t\t\t\t\tvalue: domainVerificationToken as string,\n\t\t\t\t\t\texpiresAt: new Date(Date.now() + 3600 * 24 * 7 * 1000), // 1 week\n\t\t\t\t\t},\n\t\t\t\t});\n\t\t\t}\n\n\t\t\ttype SSOProviderResponse = {\n\t\t\t\tredirectURI: string;\n\t\t\t\toidcConfig: OIDCConfig | null;\n\t\t\t\tsamlConfig: SAMLConfig | null;\n\t\t\t} & Omit<SSOProvider<O>, \"oidcConfig\" | \"samlConfig\">;\n\n\t\t\ttype SSOProviderReturn = O[\"domainVerification\"] extends { enabled: true }\n\t\t\t\t? SSOProviderResponse & {\n\t\t\t\t\t\tdomainVerified: boolean;\n\t\t\t\t\t\tdomainVerificationToken: string;\n\t\t\t\t\t}\n\t\t\t\t: SSOProviderResponse;\n\n\t\t\tconst result = {\n\t\t\t\t...provider,\n\t\t\t\toidcConfig: safeJsonParse<OIDCConfig>(\n\t\t\t\t\tprovider.oidcConfig as unknown as string,\n\t\t\t\t),\n\t\t\t\tsamlConfig: safeJsonParse<SAMLConfig>(\n\t\t\t\t\tprovider.samlConfig as unknown as string,\n\t\t\t\t),\n\t\t\t\tredirectURI: `${ctx.context.baseURL}/sso/callback/${provider.providerId}`,\n\t\t\t\t...(options?.domainVerification?.enabled ? { domainVerified } : {}),\n\t\t\t\t...(options?.domainVerification?.enabled\n\t\t\t\t\t? { domainVerificationToken }\n\t\t\t\t\t: {}),\n\t\t\t};\n\n\t\t\treturn ctx.json(result as SSOProviderReturn);\n\t\t},\n\t);\n};\n\nconst signInSSOBodySchema = z.object({\n\temail: z\n\t\t.string({})\n\t\t.meta({\n\t\t\tdescription:\n\t\t\t\t\"The email address to sign in with. This is used to identify the issuer to sign in with. It's optional if the issuer is provided\",\n\t\t})\n\t\t.optional(),\n\torganizationSlug: z\n\t\t.string({})\n\t\t.meta({\n\t\t\tdescription: \"The slug of the organization to sign in with\",\n\t\t})\n\t\t.optional(),\n\tproviderId: z\n\t\t.string({})\n\t\t.meta({\n\t\t\tdescription:\n\t\t\t\t\"The ID of the provider to sign in with. This can be provided instead of email or issuer\",\n\t\t})\n\t\t.optional(),\n\tdomain: z\n\t\t.string({})\n\t\t.meta({\n\t\t\tdescription: \"The domain of the provider.\",\n\t\t})\n\t\t.optional(),\n\tcallbackURL: z.string({}).meta({\n\t\tdescription: \"The URL to redirect to after login\",\n\t}),\n\terrorCallbackURL: z\n\t\t.string({})\n\t\t.meta({\n\t\t\tdescription: \"The URL to redirect to after login\",\n\t\t})\n\t\t.optional(),\n\tnewUserCallbackURL: z\n\t\t.string({})\n\t\t.meta({\n\t\t\tdescription: \"The URL to redirect to after login if the user is new\",\n\t\t})\n\t\t.optional(),\n\tscopes: z\n\t\t.array(z.string(), {})\n\t\t.meta({\n\t\t\tdescription: \"Scopes to request from the provider.\",\n\t\t})\n\t\t.optional(),\n\tloginHint: z\n\t\t.string({})\n\t\t.meta({\n\t\t\tdescription:\n\t\t\t\t\"Login hint to send to the identity provider (e.g., email or identifier). If supported, will be sent as 'login_hint'.\",\n\t\t})\n\t\t.optional(),\n\trequestSignUp: z\n\t\t.boolean({})\n\t\t.meta({\n\t\t\tdescription:\n\t\t\t\t\"Explicitly request sign-up. Useful when disableImplicitSignUp is true for this provider\",\n\t\t})\n\t\t.optional(),\n\tproviderType: z.enum([\"oidc\", \"saml\"]).optional(),\n});\n\nexport const signInSSO = (options?: SSOOptions) => {\n\treturn createAuthEndpoint(\n\t\t\"/sign-in/sso\",\n\t\t{\n\t\t\tmethod: \"POST\",\n\t\t\tbody: signInSSOBodySchema,\n\t\t\tmetadata: {\n\t\t\t\topenapi: {\n\t\t\t\t\toperationId: \"signInWithSSO\",\n\t\t\t\t\tsummary: \"Sign in with SSO provider\",\n\t\t\t\t\tdescription:\n\t\t\t\t\t\t\"This endpoint is used to sign in with an SSO provider. It redirects to the provider's authorization URL\",\n\t\t\t\t\trequestBody: {\n\t\t\t\t\t\tcontent: {\n\t\t\t\t\t\t\t\"application/json\": {\n\t\t\t\t\t\t\t\tschema: {\n\t\t\t\t\t\t\t\t\ttype: \"object\",\n\t\t\t\t\t\t\t\t\tproperties: {\n\t\t\t\t\t\t\t\t\t\temail: {\n\t\t\t\t\t\t\t\t\t\t\ttype: \"string\",\n\t\t\t\t\t\t\t\t\t\t\tdescription:\n\t\t\t\t\t\t\t\t\t\t\t\t\"The email address to sign in with. This is used to identify the issuer to sign in with. It's optional if the issuer is provided\",\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\tissuer: {\n\t\t\t\t\t\t\t\t\t\t\ttype: \"string\",\n\t\t\t\t\t\t\t\t\t\t\tdescription:\n\t\t\t\t\t\t\t\t\t\t\t\t\"The issuer identifier, this is the URL of the provider and can be used to verify the provider and identify the provider during login. It's optional if the email is provided\",\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\tproviderId: {\n\t\t\t\t\t\t\t\t\t\t\ttype: \"string\",\n\t\t\t\t\t\t\t\t\t\t\tdescription:\n\t\t\t\t\t\t\t\t\t\t\t\t\"The ID of the provider to sign in with. This can be provided instead of email or issuer\",\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\tcallbackURL: {\n\t\t\t\t\t\t\t\t\t\t\ttype: \"string\",\n\t\t\t\t\t\t\t\t\t\t\tdescription: \"The URL to redirect to after login\",\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\terrorCallbackURL: {\n\t\t\t\t\t\t\t\t\t\t\ttype: \"string\",\n\t\t\t\t\t\t\t\t\t\t\tdescription: \"The URL to redirect to after login\",\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\tnewUserCallbackURL: {\n\t\t\t\t\t\t\t\t\t\t\ttype: \"string\",\n\t\t\t\t\t\t\t\t\t\t\tdescription:\n\t\t\t\t\t\t\t\t\t\t\t\t\"The URL to redirect to after login if the user is new\",\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\tloginHint: {\n\t\t\t\t\t\t\t\t\t\t\ttype: \"string\",\n\t\t\t\t\t\t\t\t\t\t\tdescription:\n\t\t\t\t\t\t\t\t\t\t\t\t\"Login hint to send to the identity provider (e.g., email or identifier). If supported, sent as 'login_hint'.\",\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\trequired: [\"callbackURL\"],\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t\tresponses: {\n\t\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\tdescription:\n\t\t\t\t\t\t\t\t\"Authorization URL generated successfully for SSO sign-in\",\n\t\t\t\t\t\t\tcontent: {\n\t\t\t\t\t\t\t\t\"application/json\": {\n\t\t\t\t\t\t\t\t\tschema: {\n\t\t\t\t\t\t\t\t\t\ttype: \"object\",\n\t\t\t\t\t\t\t\t\t\tproperties: {\n\t\t\t\t\t\t\t\t\t\t\turl: {\n\t\t\t\t\t\t\t\t\t\t\t\ttype: \"string\",\n\t\t\t\t\t\t\t\t\t\t\t\tformat: \"uri\",\n\t\t\t\t\t\t\t\t\t\t\t\tdescription:\n\t\t\t\t\t\t\t\t\t\t\t\t\t\"The authorization URL to redirect the user to for SSO sign-in\",\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\tredirect: {\n\t\t\t\t\t\t\t\t\t\t\t\ttype: \"boolean\",\n\t\t\t\t\t\t\t\t\t\t\t\tdescription:\n\t\t\t\t\t\t\t\t\t\t\t\t\t\"Indicates that the client should redirect to the provided URL\",\n\t\t\t\t\t\t\t\t\t\t\t\tenum: [true],\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\trequired: [\"url\", \"redirect\"],\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\tasync (ctx) => {\n\t\t\tconst body = ctx.body;\n\t\t\tlet { email, organizationSlug, providerId, domain } = body;\n\t\t\tif (\n\t\t\t\t!options?.defaultSSO?.length &&\n\t\t\t\t!email &&\n\t\t\t\t!organizationSlug &&\n\t\t\t\t!domain &&\n\t\t\t\t!providerId\n\t\t\t) {\n\t\t\t\tthrow new APIError(\"BAD_REQUEST\", {\n\t\t\t\t\tmessage: \"email, organizationSlug, domain or providerId is required\",\n\t\t\t\t});\n\t\t\t}\n\t\t\tdomain = body.domain || email?.split(\"@\")[1];\n\t\t\tlet orgId = \"\";\n\t\t\tif (organizationSlug) {\n\t\t\t\torgId = await ctx.context.adapter\n\t\t\t\t\t.findOne<{ id: string }>({\n\t\t\t\t\t\tmodel: \"organization\",\n\t\t\t\t\t\twhere: [\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tfield: \"slug\",\n\t\t\t\t\t\t\t\tvalue: organizationSlug,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t],\n\t\t\t\t\t})\n\t\t\t\t\t.then((res) => {\n\t\t\t\t\t\tif (!res) {\n\t\t\t\t\t\t\treturn \"\";\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn res.id;\n\t\t\t\t\t});\n\t\t\t}\n\t\t\tlet provider: SSOProvider<SSOOptions> | null = null;\n\t\t\tif (options?.defaultSSO?.length) {\n\t\t\t\t// Find matching default SSO provider by providerId\n\t\t\t\tconst matchingDefault = providerId\n\t\t\t\t\t? options.defaultSSO.find(\n\t\t\t\t\t\t\t(defaultProvider) => defaultProvider.providerId === providerId,\n\t\t\t\t\t\t)\n\t\t\t\t\t: options.defaultSSO.find(\n\t\t\t\t\t\t\t(defaultProvider) => defaultProvider.domain === domain,\n\t\t\t\t\t\t);\n\n\t\t\t\tif (matchingDefault) {\n\t\t\t\t\tprovider = {\n\t\t\t\t\t\tissuer:\n\t\t\t\t\t\t\tmatchingDefault.samlConfig?.issuer ||\n\t\t\t\t\t\t\tmatchingDefault.oidcConfig?.issuer ||\n\t\t\t\t\t\t\t\"\",\n\t\t\t\t\t\tproviderId: matchingDefault.providerId,\n\t\t\t\t\t\tuserId: \"default\",\n\t\t\t\t\t\toidcConfig: matchingDefault.oidcConfig,\n\t\t\t\t\t\tsamlConfig: matchingDefault.samlConfig,\n\t\t\t\t\t\tdomain: matchingDefault.domain,\n\t\t\t\t\t\t...(options.domainVerification?.enabled\n\t\t\t\t\t\t\t? { domainVerified: true }\n\t\t\t\t\t\t\t: {}),\n\t\t\t\t\t} as SSOProvider<SSOOptions>;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (!providerId && !orgId && !domain) {\n\t\t\t\tthrow new APIError(\"BAD_REQUEST\", {\n\t\t\t\t\tmessage: \"providerId, orgId or domain is required\",\n\t\t\t\t});\n\t\t\t}\n\t\t\t// Try to find provider in database\n\t\t\tif (!provider) {\n\t\t\t\tconst parseProvider = (res: SSOProvider<SSOOptions> | null) => {\n\t\t\t\t\tif (!res) return null;\n\t\t\t\t\treturn {\n\t\t\t\t\t\t...res,\n\t\t\t\t\t\toidcConfig: res.oidcConfig\n\t\t\t\t\t\t\t? safeJsonParse<OIDCConfig>(\n\t\t\t\t\t\t\t\t\tres.oidcConfig as unknown as string,\n\t\t\t\t\t\t\t\t) || undefined\n\t\t\t\t\t\t\t: undefined,\n\t\t\t\t\t\tsamlConfig: res.samlConfig\n\t\t\t\t\t\t\t? safeJsonParse<SAMLConfig>(\n\t\t\t\t\t\t\t\t\tres.samlConfig as unknown as string,\n\t\t\t\t\t\t\t\t) || undefined\n\t\t\t\t\t\t\t: undefined,\n\t\t\t\t\t};\n\t\t\t\t};\n\n\t\t\t\tif (providerId || orgId) {\n\t\t\t\t\t// Exact match for providerId or orgId\n\t\t\t\t\tprovider = parseProvider(\n\t\t\t\t\t\tawait ctx.context.adapter.findOne<SSOProvider<SSOOptions>>({\n\t\t\t\t\t\t\tmodel: \"ssoProvider\",\n\t\t\t\t\t\t\twhere: [\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tfield: providerId ? \"providerId\" : \"organizationId\",\n\t\t\t\t\t\t\t\t\tvalue: providerId || orgId!,\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t}),\n\t\t\t\t\t);\n\t\t\t\t} else if (domain) {\n\t\t\t\t\t// For domain lookup, support comma-separated domains\n\t\t\t\t\t// First try exact match (fast path)\n\t\t\t\t\tprovider = parseProvider(\n\t\t\t\t\t\tawait ctx.context.adapter.findOne<SSOProvider<SSOOptions>>({\n\t\t\t\t\t\t\tmodel: \"ssoProvider\",\n\t\t\t\t\t\t\twhere: [{ field: \"domain\", value: domain }],\n\t\t\t\t\t\t}),\n\t\t\t\t\t);\n\t\t\t\t\t// If not found, search all providers for comma-separated domain match\n\t\t\t\t\tif (!provider) {\n\t\t\t\t\t\tconst allProviders = await ctx.context.adapter.findMany<\n\t\t\t\t\t\t\tSSOProvider<SSOOptions>\n\t\t\t\t\t\t>({\n\t\t\t\t\t\t\tmodel: \"ssoProvider\",\n\t\t\t\t\t\t});\n\t\t\t\t\t\tconst matchingProvider = allProviders.find((p) =>\n\t\t\t\t\t\t\tdomainMatches(domain, p.domain),\n\t\t\t\t\t\t);\n\t\t\t\t\t\tprovider = parseProvider(matchingProvider ?? null);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (!provider) {\n\t\t\t\tthrow new APIError(\"NOT_FOUND\", {\n\t\t\t\t\tmessage: \"No provider found for the issuer\",\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tif (body.providerType) {\n\t\t\t\tif (body.providerType === \"oidc\" && !provider.oidcConfig) {\n\t\t\t\t\tthrow new APIError(\"BAD_REQUEST\", {\n\t\t\t\t\t\tmessage: \"OIDC provider is not configured\",\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\tif (body.providerType === \"saml\" && !provider.samlConfig) {\n\t\t\t\t\tthrow new APIError(\"BAD_REQUEST\", {\n\t\t\t\t\t\tmessage: \"SAML provider is not configured\",\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (\n\t\t\t\toptions?.domainVerification?.enabled &&\n\t\t\t\t!(\"domainVerified\" in provider && provider.domainVerified)\n\t\t\t) {\n\t\t\t\tthrow new APIError(\"UNAUTHORIZED\", {\n\t\t\t\t\tmessage: \"Provider domain has not been verified\",\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tif (provider.oidcConfig && body.providerType !== \"saml\") {\n\t\t\t\tlet finalAuthUrl = provider.oidcConfig.authorizationEndpoint;\n\t\t\t\tif (!finalAuthUrl && provider.oidcConfig.discoveryEndpoint) {\n\t\t\t\t\tconst discovery = await betterFetch<{\n\t\t\t\t\t\tauthorization_endpoint: string;\n\t\t\t\t\t}>(provider.oidcConfig.discoveryEndpoint, {\n\t\t\t\t\t\tmethod: \"GET\",\n\t\t\t\t\t});\n\t\t\t\t\tif (discovery.data) {\n\t\t\t\t\t\tfinalAuthUrl = discovery.data.authorization_endpoint;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (!finalAuthUrl) {\n\t\t\t\t\tthrow new APIError(\"BAD_REQUEST\", {\n\t\t\t\t\t\tmessage: \"Invalid OIDC configuration. Authorization URL not found.\",\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\tconst state = await generateState(ctx, undefined, false);\n\t\t\t\tconst redirectURI = `${ctx.context.baseURL}/sso/callback/${provider.providerId}`;\n\t\t\t\tconst authorizationURL = await createAuthorizationURL({\n\t\t\t\t\tid: provider.issuer,\n\t\t\t\t\toptions: {\n\t\t\t\t\t\tclientId: provider.oidcConfig.clientId,\n\t\t\t\t\t\tclientSecret: provider.oidcConfig.clientSecret,\n\t\t\t\t\t},\n\t\t\t\t\tredirectURI,\n\t\t\t\t\tstate: state.state,\n\t\t\t\t\tcodeVerifier: provider.oidcConfig.pkce\n\t\t\t\t\t\t? state.codeVerifier\n\t\t\t\t\t\t: undefined,\n\t\t\t\t\tscopes: ctx.body.scopes ||\n\t\t\t\t\t\tprovider.oidcConfig.scopes || [\n\t\t\t\t\t\t\t\"openid\",\n\t\t\t\t\t\t\t\"email\",\n\t\t\t\t\t\t\t\"profile\",\n\t\t\t\t\t\t\t\"offline_access\",\n\t\t\t\t\t\t],\n\t\t\t\t\tloginHint: ctx.body.loginHint || email,\n\t\t\t\t\tauthorizationEndpoint: finalAuthUrl,\n\t\t\t\t});\n\t\t\t\treturn ctx.json({\n\t\t\t\t\turl: authorizationURL.toString(),\n\t\t\t\t\tredirect: true,\n\t\t\t\t});\n\t\t\t}\n\t\t\tif (provider.samlConfig) {\n\t\t\t\tconst parsedSamlConfig =\n\t\t\t\t\ttypeof provider.samlConfig === \"object\"\n\t\t\t\t\t\t? provider.samlConfig\n\t\t\t\t\t\t: safeJsonParse<SAMLConfig>(\n\t\t\t\t\t\t\t\tprovider.samlConfig as unknown as string,\n\t\t\t\t\t\t\t);\n\t\t\t\tif (!parsedSamlConfig) {\n\t\t\t\t\tthrow new APIError(\"BAD_REQUEST\", {\n\t\t\t\t\t\tmessage: \"Invalid SAML configuration\",\n\t\t\t\t\t});\n\t\t\t\t}\n\n\t\t\t\tif (\n\t\t\t\t\tparsedSamlConfig.authnRequestsSigned &&\n\t\t\t\t\t!parsedSamlConfig.spMetadata?.privateKey &&\n\t\t\t\t\t!parsedSamlConfig.privateKey\n\t\t\t\t) {\n\t\t\t\t\tctx.context.logger.warn(\n\t\t\t\t\t\t\"authnRequestsSigned is enabled but no privateKey provided - AuthnRequests will not be signed\",\n\t\t\t\t\t\t{ providerId: provider.providerId },\n\t\t\t\t\t);\n\t\t\t\t}\n\n\t\t\t\tlet metadata = parsedSamlConfig.spMetadata.metadata;\n\n\t\t\t\tif (!metadata) {\n\t\t\t\t\tmetadata =\n\t\t\t\t\t\tsaml\n\t\t\t\t\t\t\t.SPMetadata({\n\t\t\t\t\t\t\t\tentityID:\n\t\t\t\t\t\t\t\t\tparsedSamlConfig.spMetadata?.entityID ||\n\t\t\t\t\t\t\t\t\tparsedSamlConfig.issuer,\n\t\t\t\t\t\t\t\tassertionConsumerService: [\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\tBinding: \"urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST\",\n\t\t\t\t\t\t\t\t\t\tLocation:\n\t\t\t\t\t\t\t\t\t\t\tparsedSamlConfig.callbackUrl ||\n\t\t\t\t\t\t\t\t\t\t\t`${ctx.context.baseURL}/sso/saml2/sp/acs/${provider.providerId}`,\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t\twantMessageSigned:\n\t\t\t\t\t\t\t\t\tparsedSamlConfig.wantAssertionsSigned || false,\n\t\t\t\t\t\t\t\tauthnRequestsSigned:\n\t\t\t\t\t\t\t\t\tparsedSamlConfig.authnRequestsSigned || false,\n\t\t\t\t\t\t\t\tnameIDFormat: parsedSamlConfig.identifierFormat\n\t\t\t\t\t\t\t\t\t? [parsedSamlConfig.identifierFormat]\n\t\t\t\t\t\t\t\t\t: undefined,\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t.getMetadata() || \"\";\n\t\t\t\t}\n\n\t\t\t\tconst sp = saml.ServiceProvider({\n\t\t\t\t\tmetadata: metadata,\n\t\t\t\t\tallowCreate: true,\n\t\t\t\t\tprivateKey:\n\t\t\t\t\t\tparsedSamlConfig.spMetadata?.privateKey ||\n\t\t\t\t\t\tparsedSamlConfig.privateKey,\n\t\t\t\t\tprivateKeyPass: parsedSamlConfig.spMetadata?.privateKeyPass,\n\t\t\t\t});\n\n\t\t\t\tconst idp = saml.IdentityProvider({\n\t\t\t\t\tmetadata: parsedSamlConfig.idpMetadata?.metadata,\n\t\t\t\t\tentityID: parsedSamlConfig.idpMetadata?.entityID,\n\t\t\t\t\tencryptCert: parsedSamlConfig.idpMetadata?.cert,\n\t\t\t\t\tsingleSignOnService:\n\t\t\t\t\t\tparsedSamlConfig.idpMetadata?.singleSignOnService,\n\t\t\t\t});\n\t\t\t\tconst loginRequest = sp.createLoginRequest(\n\t\t\t\t\tidp,\n\t\t\t\t\t\"redirect\",\n\t\t\t\t) as BindingContext & {\n\t\t\t\t\tentityEndpoint: string;\n\t\t\t\t\ttype: string;\n\t\t\t\t\tid: string;\n\t\t\t\t};\n\t\t\t\tif (!loginRequest) {\n\t\t\t\t\tthrow new APIError(\"BAD_REQUEST\", {\n\t\t\t\t\t\tmessage: \"Invalid SAML request\",\n\t\t\t\t\t});\n\t\t\t\t}\n\n\t\t\t\tconst { state: relayState } = await generateRelayState(\n\t\t\t\t\tctx,\n\t\t\t\t\tundefined,\n\t\t\t\t\tfalse,\n\t\t\t\t);\n\n\t\t\t\tconst shouldSaveRequest =\n\t\t\t\t\tloginRequest.id && options?.saml?.enableInResponseToValidation;\n\t\t\t\tif (shouldSaveRequest) {\n\t\t\t\t\tconst ttl = options?.saml?.requestTTL ?? DEFAULT_AUTHN_REQUEST_TTL_MS;\n\t\t\t\t\tconst record: AuthnRequestRecord = {\n\t\t\t\t\t\tid: loginRequest.id,\n\t\t\t\t\t\tproviderId: provider.providerId,\n\t\t\t\t\t\tcreatedAt: Date.now(),\n\t\t\t\t\t\texpiresAt: Date.now() + ttl,\n\t\t\t\t\t};\n\t\t\t\t\tawait ctx.context.internalAdapter.createVerificationValue({\n\t\t\t\t\t\tidentifier: `${AUTHN_REQUEST_KEY_PREFIX}${record.id}`,\n\t\t\t\t\t\tvalue: JSON.stringify(record),\n\t\t\t\t\t\texpiresAt: new Date(record.expiresAt),\n\t\t\t\t\t});\n\t\t\t\t}\n\n\t\t\t\treturn ctx.json({\n\t\t\t\t\turl: `${loginRequest.context}&RelayState=${encodeURIComponent(relayState)}`,\n\t\t\t\t\tredirect: true,\n\t\t\t\t});\n\t\t\t}\n\t\t\tthrow new APIError(\"BAD_REQUEST\", {\n\t\t\t\tmessage: \"Invalid SSO provider\",\n\t\t\t});\n\t\t},\n\t);\n};\n\nconst callbackSSOQuerySchema = z.object({\n\tcode: z.string().optional(),\n\tstate: z.string(),\n\terror: z.string().optional(),\n\terror_description: z.string().optional(),\n});\n\nexport const callbackSSO = (options?: SSOOptions) => {\n\treturn createAuthEndpoint(\n\t\t\"/sso/callback/:providerId\",\n\t\t{\n\t\t\tmethod: \"GET\",\n\t\t\tquery: callbackSSOQuerySchema,\n\t\t\tallowedMediaTypes: [\n\t\t\t\t\"application/x-www-form-urlencoded\",\n\t\t\t\t\"application/json\",\n\t\t\t],\n\t\t\tmetadata: {\n\t\t\t\t...HIDE_METADATA,\n\t\t\t\topenapi: {\n\t\t\t\t\toperationId: \"handleSSOCallback\",\n\t\t\t\t\tsummary: \"Callback URL for SSO provider\",\n\t\t\t\t\tdescription:\n\t\t\t\t\t\t\"This endpoint is used as the callback URL for SSO providers. It handles the authorization code and exchanges it for an access token\",\n\t\t\t\t\tresponses: {\n\t\t\t\t\t\t\"302\": {\n\t\t\t\t\t\t\tdescription: \"Redirects to the callback URL\",\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\tasync (ctx) => {\n\t\t\tconst { code, error, error_description } = ctx.query;\n\t\t\tconst stateData = await parseState(ctx);\n\t\t\tif (!stateData) {\n\t\t\t\tconst errorURL =\n\t\t\t\t\tctx.context.options.onAPIError?.errorURL ||\n\t\t\t\t\t`${ctx.context.baseURL}/error`;\n\t\t\t\tthrow ctx.redirect(`${errorURL}?error=invalid_state`);\n\t\t\t}\n\t\t\tconst { callbackURL, errorURL, newUserURL, requestSignUp } = stateData;\n\t\t\tif (!code || error) {\n\t\t\t\tthrow ctx.redirect(\n\t\t\t\t\t`${\n\t\t\t\t\t\terrorURL || callbackURL\n\t\t\t\t\t}?error=${error}&error_description=${error_description}`,\n\t\t\t\t);\n\t\t\t}\n\t\t\tlet provider: SSOProvider<SSOOptions> | null = null;\n\t\t\tif (options?.defaultSSO?.length) {\n\t\t\t\tconst matchingDefault = options.defaultSSO.find(\n\t\t\t\t\t(defaultProvider) =>\n\t\t\t\t\t\tdefaultProvider.providerId === ctx.params.providerId,\n\t\t\t\t);\n\t\t\t\tif (matchingDefault) {\n\t\t\t\t\tprovider = {\n\t\t\t\t\t\t...matchingDefault,\n\t\t\t\t\t\tissuer: matchingDefault.oidcConfig?.issuer || \"\",\n\t\t\t\t\t\tuserId: \"default\",\n\t\t\t\t\t\t...(options.domainVerification?.enabled\n\t\t\t\t\t\t\t? { domainVerified: true }\n\t\t\t\t\t\t\t: {}),\n\t\t\t\t\t} as SSOProvider<SSOOptions>;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (!provider) {\n\t\t\t\tprovider = await ctx.context.adapter\n\t\t\t\t\t.findOne<{\n\t\t\t\t\t\toidcConfig: string;\n\t\t\t\t\t}>({\n\t\t\t\t\t\tmodel: \"ssoProvider\",\n\t\t\t\t\t\twhere: [\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tfield: \"providerId\",\n\t\t\t\t\t\t\t\tvalue: ctx.params.providerId,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t],\n\t\t\t\t\t})\n\t\t\t\t\t.then((res) => {\n\t\t\t\t\t\tif (!res) {\n\t\t\t\t\t\t\treturn null;\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\t...res,\n\t\t\t\t\t\t\toidcConfig:\n\t\t\t\t\t\t\t\tsafeJsonParse<OIDCConfig>(res.oidcConfig) || undefined,\n\t\t\t\t\t\t} as SSOProvider<SSOOptions>;\n\t\t\t\t\t});\n\t\t\t}\n\t\t\tif (!provider) {\n\t\t\t\tthrow ctx.redirect(\n\t\t\t\t\t`${\n\t\t\t\t\t\terrorURL || callbackURL\n\t\t\t\t\t}/error?error=invalid_provider&error_description=provider not found`,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tif (\n\t\t\t\toptions?.domainVerification?.enabled &&\n\t\t\t\t!(\"domainVerified\" in provider && provider.domainVerified)\n\t\t\t) {\n\t\t\t\tthrow new APIError(\"UNAUTHORIZED\", {\n\t\t\t\t\tmessage: \"Provider domain has not been verified\",\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tlet config = provider.oidcConfig;\n\n\t\t\tif (!config) {\n\t\t\t\tthrow ctx.redirect(\n\t\t\t\t\t`${\n\t\t\t\t\t\terrorURL || callbackURL\n\t\t\t\t\t}/error?error=invalid_provider&error_description=provider not found`,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst discovery = await betterFetch<{\n\t\t\t\ttoken_endpoint: string;\n\t\t\t\tuserinfo_endpoint: string;\n\t\t\t\ttoken_endpoint_auth_method:\n\t\t\t\t\t| \"client_secret_basic\"\n\t\t\t\t\t| \"client_secret_post\";\n\t\t\t}>(config.discoveryEndpoint);\n\n\t\t\tif (discovery.data) {\n\t\t\t\tconfig = {\n\t\t\t\t\ttokenEndpoint: discovery.data.token_endpoint,\n\t\t\t\t\ttokenEndpointAuthentication:\n\t\t\t\t\t\tdiscovery.data.token_endpoint_auth_method,\n\t\t\t\t\tuserInfoEndpoint: discovery.data.userinfo_endpoint,\n\t\t\t\t\tscopes: [\"openid\", \"email\", \"profile\", \"offline_access\"],\n\t\t\t\t\t...config,\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tif (!config.tokenEndpoint) {\n\t\t\t\tthrow ctx.redirect(\n\t\t\t\t\t`${\n\t\t\t\t\t\terrorURL || callbackURL\n\t\t\t\t\t}/error?error=invalid_provider&error_description=token_endpoint_not_found`,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst tokenResponse = await validateAuthorizationCode({\n\t\t\t\tcode,\n\t\t\t\tcodeVerifier: config.pkce ? stateData.codeVerifier : undefined,\n\t\t\t\tredirectURI: `${ctx.context.baseURL}/sso/callback/${provider.providerId}`,\n\t\t\t\toptions: {\n\t\t\t\t\tclientId: config.clientId,\n\t\t\t\t\tclientSecret: config.clientSecret,\n\t\t\t\t},\n\t\t\t\ttokenEndpoint: config.tokenEndpoint,\n\t\t\t\tauthentication:\n\t\t\t\t\tconfig.tokenEndpointAuthentication === \"client_secret_post\"\n\t\t\t\t\t\t? \"post\"\n\t\t\t\t\t\t: \"basic\",\n\t\t\t}).catch((e) => {\n\t\t\t\tif (e instanceof BetterFetchError) {\n\t\t\t\t\tthrow ctx.redirect(\n\t\t\t\t\t\t`${\n\t\t\t\t\t\t\terrorURL || callbackURL\n\t\t\t\t\t\t}?error=invalid_provider&error_description=${e.message}`,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\treturn null;\n\t\t\t});\n\t\t\tif (!tokenResponse) {\n\t\t\t\tthrow ctx.redirect(\n\t\t\t\t\t`${\n\t\t\t\t\t\terrorURL || callbackURL\n\t\t\t\t\t}/error?error=invalid_provider&error_description=token_response_not_found`,\n\t\t\t\t);\n\t\t\t}\n\t\t\tlet userInfo: {\n\t\t\t\tid?: string;\n\t\t\t\temail?: string;\n\t\t\t\tname?: string;\n\t\t\t\timage?: string;\n\t\t\t\temailVerified?: boolean;\n\t\t\t\t[key: string]: any;\n\t\t\t} | null = null;\n\t\t\tif (tokenResponse.idToken) {\n\t\t\t\tconst idToken = decodeJwt(tokenResponse.idToken);\n\t\t\t\tif (!config.jwksEndpoint) {\n\t\t\t\t\tthrow ctx.redirect(\n\t\t\t\t\t\t`${\n\t\t\t\t\t\t\terrorURL || callbackURL\n\t\t\t\t\t\t}/error?error=invalid_provider&error_description=jwks_endpoint_not_found`,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tconst verified = await validateToken(\n\t\t\t\t\ttokenResponse.idToken,\n\t\t\t\t\tconfig.jwksEndpoint,\n\t\t\t\t).catch((e) => {\n\t\t\t\t\tctx.context.logger.error(e);\n\t\t\t\t\treturn null;\n\t\t\t\t});\n\t\t\t\tif (!verified) {\n\t\t\t\t\tthrow ctx.redirect(\n\t\t\t\t\t\t`${\n\t\t\t\t\t\t\terrorURL || callbackURL\n\t\t\t\t\t\t}/error?error=invalid_provider&error_description=token_not_verified`,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tif (verified.payload.iss !== provider.issuer) {\n\t\t\t\t\tthrow ctx.redirect(\n\t\t\t\t\t\t`${\n\t\t\t\t\t\t\terrorURL || callbackURL\n\t\t\t\t\t\t}/error?error=invalid_provider&error_description=issuer_mismatch`,\n\t\t\t\t\t);\n\t\t\t\t}\n\n\t\t\t\tconst mapping = config.mapping || {};\n\t\t\t\tuserInfo = {\n\t\t\t\t\t...Object.fromEntries(\n\t\t\t\t\t\tObject.entries(mapping.extraFields || {}).map(([key, value]) => [\n\t\t\t\t\t\t\tkey,\n\t\t\t\t\t\t\tverified.payload[value],\n\t\t\t\t\t\t]),\n\t\t\t\t\t),\n\t\t\t\t\tid: idToken[mapping.id || \"sub\"],\n\t\t\t\t\temail: idToken[mapping.email || \"email\"],\n\t\t\t\t\temailVerified: options?.trustEmailVerified\n\t\t\t\t\t\t? idToken[mapping.emailVerified || \"email_verified\"]\n\t\t\t\t\t\t: false,\n\t\t\t\t\tname: idToken[mapping.name || \"name\"],\n\t\t\t\t\timage: idToken[mapping.image || \"picture\"],\n\t\t\t\t} as {\n\t\t\t\t\tid?: string;\n\t\t\t\t\temail?: string;\n\t\t\t\t\tname?: string;\n\t\t\t\t\timage?: string;\n\t\t\t\t\temailVerified?: boolean;\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tif (!userInfo) {\n\t\t\t\tif (!config.userInfoEndpoint) {\n\t\t\t\t\tthrow ctx.redirect(\n\t\t\t\t\t\t`${\n\t\t\t\t\t\t\terrorURL || callbackURL\n\t\t\t\t\t\t}/error?error=invalid_provider&error_description=user_info_endpoint_not_found`,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tconst userInfoResponse = await betterFetch<{\n\t\t\t\t\temail?: string;\n\t\t\t\t\tname?: string;\n\t\t\t\t\tid?: string;\n\t\t\t\t\timage?: string;\n\t\t\t\t\temailVerified?: boolean;\n\t\t\t\t}>(config.userInfoEndpoint, {\n\t\t\t\t\theaders: {\n\t\t\t\t\t\tAuthorization: `Bearer ${tokenResponse.accessToken}`,\n\t\t\t\t\t},\n\t\t\t\t});\n\t\t\t\tif (userInfoResponse.error) {\n\t\t\t\t\tthrow ctx.redirect(\n\t\t\t\t\t\t`${\n\t\t\t\t\t\t\terrorURL || callbackURL\n\t\t\t\t\t\t}/error?error=invalid_provider&error_description=${\n\t\t\t\t\t\t\tuserInfoResponse.error.message\n\t\t\t\t\t\t}`,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tuserInfo = userInfoResponse.data;\n\t\t\t}\n\n\t\t\tif (!userInfo.email || !userInfo.id) {\n\t\t\t\tthrow ctx.redirect(\n\t\t\t\t\t`${\n\t\t\t\t\t\terrorURL || callbackURL\n\t\t\t\t\t}/error?error=invalid_provider&error_description=missing_user_info`,\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst isTrustedProvider =\n\t\t\t\t\"domainVerified\" in provider &&\n\t\t\t\t(provider as { domainVerified?: boolean }).domainVerified === true &&\n\t\t\t\tvalidateEmailDomain(userInfo.email, provider.domain);\n\n\t\t\tconst linked = await handleOAuthUserInfo(ctx, {\n\t\t\t\tuserInfo: {\n\t\t\t\t\temail: userInfo.email,\n\t\t\t\t\tname: userInfo.name || userInfo.email,\n\t\t\t\t\tid: userInfo.id,\n\t\t\t\t\timage: userInfo.image,\n\t\t\t\t\temailVerified: options?.trustEmailVerified\n\t\t\t\t\t\t? userInfo.emailVerified || false\n\t\t\t\t\t\t: false,\n\t\t\t\t},\n\t\t\t\taccount: {\n\t\t\t\t\tidToken: tokenResponse.idToken,\n\t\t\t\t\taccessToken: tokenResponse.accessToken,\n\t\t\t\t\trefreshToken: tokenResponse.refreshToken,\n\t\t\t\t\taccountId: userInfo.id,\n\t\t\t\t\tproviderId: provider.providerId,\n\t\t\t\t\taccessTokenExpiresAt: tokenResponse.accessTokenExpiresAt,\n\t\t\t\t\trefreshTokenExpiresAt: tokenResponse.refreshTokenExpiresAt,\n\t\t\t\t\tscope: tokenResponse.scopes?.join(\",\"),\n\t\t\t\t},\n\t\t\t\tcallbackURL,\n\t\t\t\tdisableSignUp: options?.disableImplicitSignUp && !requestSignUp,\n\t\t\t\toverrideUserInfo: config.overrideUserInfo,\n\t\t\t\tisTrustedProvider,\n\t\t\t});\n\t\t\tif (linked.error) {\n\t\t\t\tthrow ctx.redirect(\n\t\t\t\t\t`${errorURL || callbackURL}/error?error=${linked.error}`,\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst { session, user } = linked.data!;\n\n\t\t\tif (options?.provisionUser) {\n\t\t\t\tawait options.provisionUser({\n\t\t\t\t\tuser,\n\t\t\t\t\tuserInfo,\n\t\t\t\t\ttoken: tokenResponse,\n\t\t\t\t\tprovider,\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tawait assignOrganizationFromProvider(ctx as any, {\n\t\t\t\tuser,\n\t\t\t\tprofile: {\n\t\t\t\t\tproviderType: \"oidc\",\n\t\t\t\t\tproviderId: provider.providerId,\n\t\t\t\t\taccountId: userInfo.id,\n\t\t\t\t\temail: userInfo.email,\n\t\t\t\t\temailVerified: Boolean(userInfo.emailVerified),\n\t\t\t\t\trawAttributes: userInfo,\n\t\t\t\t},\n\t\t\t\tprovider,\n\t\t\t\ttoken: tokenResponse,\n\t\t\t\tprovisioningOptions: options?.organizationProvisioning,\n\t\t\t});\n\n\t\t\tawait setSessionCookie(ctx, {\n\t\t\t\tsession,\n\t\t\t\tuser,\n\t\t\t});\n\t\t\tlet toRedirectTo: string;\n\t\t\ttry {\n\t\t\t\tconst url = linked.isRegister ? newUserURL || callbackURL : callbackURL;\n\t\t\t\ttoRedirectTo = url.toString();\n\t\t\t} catch {\n\t\t\t\ttoRedirectTo = linked.isRegister\n\t\t\t\t\t? newUserURL || callbackURL\n\t\t\t\t\t: callbackURL;\n\t\t\t}\n\t\t\tthrow ctx.redirect(toRedirectTo);\n\t\t},\n\t);\n};\n\nconst callbackSSOSAMLBodySchema = z.object({\n\tSAMLResponse: z.string(),\n\tRelayState: z.string().optional(),\n});\n\n/**\n * Validates and returns a safe redirect URL.\n * - Prevents open redirect attacks by validating against trusted origins\n * - Prevents redirect loops by checking if URL points to callback route\n * - Falls back to appOrigin if URL is invalid or unsafe\n */\nconst getSafeRedirectUrl = (\n\turl: string | undefined,\n\tcallbackPath: string,\n\tappOrigin: string,\n\tisTrustedOrigin: (\n\t\turl: string,\n\t\tsettings?: { allowRelativePaths: boolean },\n\t) => boolean,\n): string => {\n\tif (!url) {\n\t\treturn appOrigin;\n\t}\n\n\tif (url.startsWith(\"/\") && !url.startsWith(\"//\")) {\n\t\ttry {\n\t\t\tconst absoluteUrl = new URL(url, appOrigin);\n\t\t\tif (absoluteUrl.origin !== appOrigin) {\n\t\t\t\treturn appOrigin;\n\t\t\t}\n\t\t\tconst callbackPathname = new URL(callbackPath).pathname;\n\t\t\tif (absoluteUrl.pathname === callbackPathname) {\n\t\t\t\treturn appOrigin;\n\t\t\t}\n\t\t} catch {\n\t\t\treturn appOrigin;\n\t\t}\n\t\treturn url;\n\t}\n\n\tif (!isTrustedOrigin(url, { allowRelativePaths: false })) {\n\t\treturn appOrigin;\n\t}\n\n\ttry {\n\t\tconst callbackPathname = new URL(callbackPath).pathname;\n\t\tconst urlPathname = new URL(url).pathname;\n\t\tif (urlPathname === callbackPathname) {\n\t\t\treturn appOrigin;\n\t\t}\n\t} catch {\n\t\tif (url === callbackPath || url.startsWith(`${callbackPath}?`)) {\n\t\t\treturn appOrigin;\n\t\t}\n\t}\n\n\treturn url;\n};\n\nexport const callbackSSOSAML = (options?: SSOOptions) => {\n\treturn createAuthEndpoint(\n\t\t\"/sso/saml2/callback/:providerId\",\n\t\t{\n\t\t\tmethod: [\"GET\", \"POST\"],\n\t\t\tbody: callbackSSOSAMLBodySchema.optional(),\n\t\t\tquery: z\n\t\t\t\t.object({\n\t\t\t\t\tRelayState: z.string().optional(),\n\t\t\t\t})\n\t\t\t\t.optional(),\n\t\t\tmetadata: {\n\t\t\t\t...HIDE_METADATA,\n\t\t\t\tallowedMediaTypes: [\n\t\t\t\t\t\"application/x-www-form-urlencoded\",\n\t\t\t\t\t\"application/json\",\n\t\t\t\t],\n\t\t\t\topenapi: {\n\t\t\t\t\toperationId: \"handleSAMLCallback\",\n\t\t\t\t\tsummary: \"Callback URL for SAML provider\",\n\t\t\t\t\tdescription:\n\t\t\t\t\t\t\"This endpoint is used as the callback URL for SAML providers. Supports both GET and POST methods for IdP-initiated and SP-initiated flows.\",\n\t\t\t\t\tresponses: {\n\t\t\t\t\t\t\"302\": {\n\t\t\t\t\t\t\tdescription: \"Redirects to the callback URL\",\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"400\": {\n\t\t\t\t\t\t\tdescription: \"Invalid SAML response\",\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"401\": {\n\t\t\t\t\t\t\tdescription: \"Unauthorized - SAML authentication failed\",\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\tasync (ctx) => {\n\t\t\tconst { providerId } = ctx.params;\n\t\t\tconst appOrigin = new URL(ctx.context.baseURL).origin;\n\t\t\tconst errorURL =\n\t\t\t\tctx.context.options.onAPIError?.errorURL || `${appOrigin}/error`;\n\t\t\tconst currentCallbackPath = `${ctx.context.baseURL}/sso/saml2/callback/${providerId}`;\n\n\t\t\t// Determine if this is a GET request by checking both method AND body presence\n\t\t\t// When called via auth.api.*, ctx.method may not be reliable, so we also check for body\n\t\t\tconst isGetRequest = ctx.method === \"GET\" && !ctx.body?.SAMLResponse;\n\n\t\t\tif (isGetRequest) {\n\t\t\t\tconst session = await getSessionFromCtx(ctx);\n\n\t\t\t\tif (!session?.session) {\n\t\t\t\t\tthrow ctx.redirect(`${errorURL}?error=invalid_request`);\n\t\t\t\t}\n\n\t\t\t\tconst relayState = ctx.query?.RelayState as string | undefined;\n\t\t\t\tconst safeRedirectUrl = getSafeRedirectUrl(\n\t\t\t\t\trelayState,\n\t\t\t\t\tcurrentCallbackPath,\n\t\t\t\t\tappOrigin,\n\t\t\t\t\t(url, settings) => ctx.context.isTrustedOrigin(url, settings),\n\t\t\t\t);\n\n\t\t\t\tthrow ctx.redirect(safeRedirectUrl);\n\t\t\t}\n\n\t\t\tif (!ctx.body?.SAMLResponse) {\n\t\t\t\tthrow new APIError(\"BAD_REQUEST\", {\n\t\t\t\t\tmessage: \"SAMLResponse is required for POST requests\",\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tconst { SAMLResponse } = ctx.body;\n\n\t\t\tconst maxResponseSize =\n\t\t\t\toptions?.saml?.maxResponseSize ?? DEFAULT_MAX_SAML_RESPONSE_SIZE;\n\t\t\tif (new TextEncoder().encode(SAMLResponse).length > maxResponseSize) {\n\t\t\t\tthrow new APIError(\"BAD_REQUEST\", {\n\t\t\t\t\tmessage: `SAML response exceeds maximum allowed size (${maxResponseSize} bytes)`,\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tlet relayState: RelayState | null = null;\n\t\t\tif (ctx.body.RelayState) {\n\t\t\t\ttry {\n\t\t\t\t\trelayState = await parseRelayState(ctx);\n\t\t\t\t} catch {\n\t\t\t\t\trelayState = null;\n\t\t\t\t}\n\t\t\t}\n\t\t\tlet provider: SSOProvider<SSOOptions> | null = null;\n\t\t\tif (options?.defaultSSO?.length) {\n\t\t\t\tconst matchingDefault = options.defaultSSO.find(\n\t\t\t\t\t(defaultProvider) => defaultProvider.providerId === providerId,\n\t\t\t\t);\n\t\t\t\tif (matchingDefault) {\n\t\t\t\t\tprovider = {\n\t\t\t\t\t\t...matchingDefault,\n\t\t\t\t\t\tuserId: \"default\",\n\t\t\t\t\t\tissuer: matchingDefault.samlConfig?.issuer || \"\",\n\t\t\t\t\t\t...(options.domainVerification?.enabled\n\t\t\t\t\t\t\t? { domainVerified: true }\n\t\t\t\t\t\t\t: {}),\n\t\t\t\t\t} as SSOProvider<SSOOptions>;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (!provider) {\n\t\t\t\tprovider = await ctx.context.adapter\n\t\t\t\t\t.findOne<SSOProvider<SSOOptions>>({\n\t\t\t\t\t\tmodel: \"ssoProvider\",\n\t\t\t\t\t\twhere: [{ field: \"providerId\", value: providerId }],\n\t\t\t\t\t})\n\t\t\t\t\t.then((res) => {\n\t\t\t\t\t\tif (!res) return null;\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\t...res,\n\t\t\t\t\t\t\tsamlConfig: res.samlConfig\n\t\t\t\t\t\t\t\t? safeJsonParse<SAMLConfig>(\n\t\t\t\t\t\t\t\t\t\tres.samlConfig as unknown as string,\n\t\t\t\t\t\t\t\t\t) || undefined\n\t\t\t\t\t\t\t\t: undefined,\n\t\t\t\t\t\t};\n\t\t\t\t\t});\n\t\t\t}\n\n\t\t\tif (!provider) {\n\t\t\t\tthrow new APIError(\"NOT_FOUND\", {\n\t\t\t\t\tmessage: \"No provider found for the given providerId\",\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tif (\n\t\t\t\toptions?.domainVerification?.enabled &&\n\t\t\t\t!(\"domainVerified\" in provider && provider.domainVerified)\n\t\t\t) {\n\t\t\t\tthrow new APIError(\"UNAUTHORIZED\", {\n\t\t\t\t\tmessage: \"Provider domain has not been verified\",\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tconst parsedSamlConfig = safeJsonParse<SAMLConfig>(\n\t\t\t\tprovider.samlConfig as unknown as string,\n\t\t\t);\n\t\t\tif (!parsedSamlConfig) {\n\t\t\t\tthrow new APIError(\"BAD_REQUEST\", {\n\t\t\t\t\tmessage: \"Invalid SAML configuration\",\n\t\t\t\t});\n\t\t\t}\n\t\t\tconst idpData = parsedSamlConfig.idpMetadata;\n\t\t\tlet idp: IdentityProvider | null = null;\n\n\t\t\t// Construct IDP with fallback to manual configuration\n\t\t\tif (!idpData?.metadata) {\n\t\t\t\tidp = saml.IdentityProvider({\n\t\t\t\t\tentityID: idpData?.entityID || parsedSamlConfig.issuer,\n\t\t\t\t\tsingleSignOnService: [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tBinding: \"urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect\",\n\t\t\t\t\t\t\tLocation: parsedSamlConfig.entryPoint,\n\t\t\t\t\t\t},\n\t\t\t\t\t],\n\t\t\t\t\tsigningCert: idpData?.cert || parsedSamlConfig.cert,\n\t\t\t\t\twantAuthnRequestsSigned:\n\t\t\t\t\t\tparsedSamlConfig.wantAssertionsSigned || false,\n\t\t\t\t\tisAssertionEncrypted: idpData?.isAssertionEncrypted || false,\n\t\t\t\t\tencPrivateKey: idpData?.encPrivateKey,\n\t\t\t\t\tencPrivateKeyPass: idpData?.encPrivateKeyPass,\n\t\t\t\t});\n\t\t\t} else {\n\t\t\t\tidp = saml.IdentityProvider({\n\t\t\t\t\tmetadata: idpData.metadata,\n\t\t\t\t\tprivateKey: idpData.privateKey,\n\t\t\t\t\tprivateKeyPass: idpData.privateKeyPass,\n\t\t\t\t\tisAssertionEncrypted: idpData.isAssertionEncrypted,\n\t\t\t\t\tencPrivateKey: idpData.encPrivateKey,\n\t\t\t\t\tencPrivateKeyPass: idpData.encPrivateKeyPass,\n\t\t\t\t});\n\t\t\t}\n\n\t\t\t// Construct SP with fallback to manual configuration\n\t\t\tconst spData = parsedSamlConfig.spMetadata;\n\t\t\tconst sp = saml.ServiceProvider({\n\t\t\t\tmetadata: spData?.metadata,\n\t\t\t\tentityID: spData?.entityID || parsedSamlConfig.issuer,\n\t\t\t\tassertionConsumerService: spData?.metadata\n\t\t\t\t\t? undefined\n\t\t\t\t\t: [\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tBinding: \"urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST\",\n\t\t\t\t\t\t\t\tLocation: parsedSamlConfig.callbackUrl,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t],\n\t\t\t\tprivateKey: spData?.privateKey || parsedSamlConfig.privateKey,\n\t\t\t\tprivateKeyPass: spData?.privateKeyPass,\n\t\t\t\tisAssertionEncrypted: spData?.isAssertionEncrypted || false,\n\t\t\t\tencPrivateKey: spData?.encPrivateKey,\n\t\t\t\tencPrivateKeyPass: spData?.encPrivateKeyPass,\n\t\t\t\twantMessageSigned: parsedSamlConfig.wantAssertionsSigned || false,\n\t\t\t\tnameIDFormat: parsedSamlConfig.identifierFormat\n\t\t\t\t\t? [parsedSamlConfig.identifierFormat]\n\t\t\t\t\t: undefined,\n\t\t\t});\n\n\t\t\tvalidateSingleAssertion(SAMLResponse);\n\n\t\t\tlet parsedResponse: FlowResult;\n\t\t\ttry {\n\t\t\t\tparsedResponse = await sp.parseLoginResponse(idp, \"post\", {\n\t\t\t\t\tbody: {\n\t\t\t\t\t\tSAMLResponse,\n\t\t\t\t\t\tRelayState: ctx.body.RelayState || undefined,\n\t\t\t\t\t},\n\t\t\t\t});\n\n\t\t\t\tif (!parsedResponse?.extract) {\n\t\t\t\t\tthrow new Error(\"Invalid SAML response structure\");\n\t\t\t\t}\n\t\t\t} catch (error) {\n\t\t\t\tctx.context.logger.error(\"SAML response validation failed\", {\n\t\t\t\t\terror,\n\t\t\t\t\tdecodedResponse: Buffer.from(SAMLResponse, \"base64\").toString(\n\t\t\t\t\t\t\"utf-8\",\n\t\t\t\t\t),\n\t\t\t\t});\n\t\t\t\tthrow new APIError(\"BAD_REQUEST\", {\n\t\t\t\t\tmessage: \"Invalid SAML response\",\n\t\t\t\t\tdetails: error instanceof Error ? error.message : String(error),\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tconst { extract } = parsedResponse!;\n\n\t\t\tvalidateSAMLAlgorithms(parsedResponse, options?.saml?.algorithms);\n\n\t\t\tvalidateSAMLTimestamp((extract as any).conditions, {\n\t\t\t\tclockSkew: options?.saml?.clockSkew,\n\t\t\t\trequireTimestamps: options?.saml?.requireTimestamps,\n\t\t\t\tlogger: ctx.context.logger,\n\t\t\t});\n\n\t\t\tconst inResponseTo = (extract as any).inResponseTo as string | undefined;\n\t\t\tconst shouldValidateInResponseTo =\n\t\t\t\toptions?.saml?.enableInResponseToValidation;\n\n\t\t\tif (shouldValidateInResponseTo) {\n\t\t\t\tconst allowIdpInitiated = options?.saml?.allowIdpInitiated !== false;\n\n\t\t\t\tif (inResponseTo) {\n\t\t\t\t\tlet storedRequest: AuthnRequestRecord | null = null;\n\n\t\t\t\t\tconst verification =\n\t\t\t\t\t\tawait ctx.context.internalAdapter.findVerificationValue(\n\t\t\t\t\t\t\t`${AUTHN_REQUEST_KEY_PREFIX}${inResponseTo}`,\n\t\t\t\t\t\t);\n\t\t\t\t\tif (verification) {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tstoredRequest = JSON.parse(\n\t\t\t\t\t\t\t\tverification.value,\n\t\t\t\t\t\t\t) as AuthnRequestRecord;\n\t\t\t\t\t\t\tif (storedRequest && storedRequest.expiresAt < Date.now()) {\n\t\t\t\t\t\t\t\tstoredRequest = null;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} catch {\n\t\t\t\t\t\t\tstoredRequest = null;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tif (!storedRequest) {\n\t\t\t\t\t\tctx.context.logger.error(\n\t\t\t\t\t\t\t\"SAML InResponseTo validation failed: unknown or expired request ID\",\n\t\t\t\t\t\t\t{ inResponseTo, providerId: provider.providerId },\n\t\t\t\t\t\t);\n\t\t\t\t\t\tconst redirectUrl =\n\t\t\t\t\t\t\trelayState?.callbackURL ||\n\t\t\t\t\t\t\tparsedSamlConfig.callbackUrl ||\n\t\t\t\t\t\t\tctx.context.baseURL;\n\t\t\t\t\t\tthrow ctx.redirect(\n\t\t\t\t\t\t\t`${redirectUrl}?error=invalid_saml_response&error_description=Unknown+or+expired+request+ID`,\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\n\t\t\t\t\tif (storedRequest.providerId !== provider.providerId) {\n\t\t\t\t\t\tctx.context.logger.error(\n\t\t\t\t\t\t\t\"SAML InResponseTo validation failed: provider mismatch\",\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tinResponseTo,\n\t\t\t\t\t\t\t\texpectedProvider: storedRequest.providerId,\n\t\t\t\t\t\t\t\tactualProvider: provider.providerId,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t);\n\n\t\t\t\t\t\tawait ctx.context.internalAdapter.deleteVerificationByIdentifier(\n\t\t\t\t\t\t\t`${AUTHN_REQUEST_KEY_PREFIX}${inResponseTo}`,\n\t\t\t\t\t\t);\n\t\t\t\t\t\tconst redirectUrl =\n\t\t\t\t\t\t\trelayState?.callbackURL ||\n\t\t\t\t\t\t\tparsedSamlConfig.callbackUrl ||\n\t\t\t\t\t\t\tctx.context.baseURL;\n\t\t\t\t\t\tthrow ctx.redirect(\n\t\t\t\t\t\t\t`${redirectUrl}?error=invalid_saml_response&error_description=Provider+mismatch`,\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\n\t\t\t\t\tawait ctx.context.internalAdapter.deleteVerificationByIdentifier(\n\t\t\t\t\t\t`${AUTHN_REQUEST_KEY_PREFIX}${inResponseTo}`,\n\t\t\t\t\t);\n\t\t\t\t} else if (!allowIdpInitiated) {\n\t\t\t\t\tctx.context.logger.error(\n\t\t\t\t\t\t\"SAML IdP-initiated SSO rejected: InResponseTo missing and allowIdpInitiated is false\",\n\t\t\t\t\t\t{ providerId: provider.providerId },\n\t\t\t\t\t);\n\t\t\t\t\tconst redirectUrl =\n\t\t\t\t\t\trelayState?.callbackURL ||\n\t\t\t\t\t\tparsedSamlConfig.callbackUrl ||\n\t\t\t\t\t\tctx.context.baseURL;\n\t\t\t\t\tthrow ctx.redirect(\n\t\t\t\t\t\t`${redirectUrl}?error=unsolicited_response&error_description=IdP-initiated+SSO+not+allowed`,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Assertion Replay Protection\n\t\t\tconst samlContent = (parsedResponse as any).samlContent as\n\t\t\t\t| string\n\t\t\t\t| undefined;\n\t\t\tconst assertionId = samlContent ? extractAssertionId(samlContent) : null;\n\n\t\t\tif (assertionId) {\n\t\t\t\tconst issuer = idp.entityMeta.getEntityID();\n\t\t\t\tconst conditions = (extract as any).conditions as\n\t\t\t\t\t| SAMLConditions\n\t\t\t\t\t| undefined;\n\t\t\t\tconst clockSkew = options?.saml?.clockSkew ?? DEFAULT_CLOCK_SKEW_MS;\n\t\t\t\tconst expiresAt = conditions?.notOnOrAfter\n\t\t\t\t\t? new Date(conditions.notOnOrAfter).getTime() + clockSkew\n\t\t\t\t\t: Date.now() + DEFAULT_ASSERTION_TTL_MS;\n\n\t\t\t\tconst existingAssertion =\n\t\t\t\t\tawait ctx.context.internalAdapter.findVerificationValue(\n\t\t\t\t\t\t`${USED_ASSERTION_KEY_PREFIX}${assertionId}`,\n\t\t\t\t\t);\n\n\t\t\t\tlet isReplay = false;\n\t\t\t\tif (existingAssertion) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst stored = JSON.parse(existingAssertion.value);\n\t\t\t\t\t\tif (stored.expiresAt >= Date.now()) {\n\t\t\t\t\t\t\tisReplay = true;\n\t\t\t\t\t\t}\n\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\tctx.context.logger.warn(\"Failed to parse stored assertion record\", {\n\t\t\t\t\t\t\tassertionId,\n\t\t\t\t\t\t\terror,\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (isReplay) {\n\t\t\t\t\tctx.context.logger.error(\n\t\t\t\t\t\t\"SAML assertion replay detected: assertion ID already used\",\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tassertionId,\n\t\t\t\t\t\t\tissuer,\n\t\t\t\t\t\t\tproviderId: provider.providerId,\n\t\t\t\t\t\t},\n\t\t\t\t\t);\n\t\t\t\t\tconst redirectUrl =\n\t\t\t\t\t\trelayState?.callbackURL ||\n\t\t\t\t\t\tparsedSamlConfig.callbackUrl ||\n\t\t\t\t\t\tctx.context.baseURL;\n\t\t\t\t\tthrow ctx.redirect(\n\t\t\t\t\t\t`${redirectUrl}?error=replay_detected&error_description=SAML+assertion+has+already+been+used`,\n\t\t\t\t\t);\n\t\t\t\t}\n\n\t\t\t\tawait ctx.context.internalAdapter.createVerificationValue({\n\t\t\t\t\tidentifier: `${USED_ASSERTION_KEY_PREFIX}${assertionId}`,\n\t\t\t\t\tvalue: JSON.stringify({\n\t\t\t\t\t\tassertionId,\n\t\t\t\t\t\tissuer,\n\t\t\t\t\t\tproviderId: provider.providerId,\n\t\t\t\t\t\tusedAt: Date.now(),\n\t\t\t\t\t\texpiresAt,\n\t\t\t\t\t}),\n\t\t\t\t\texpiresAt: new Date(expiresAt),\n\t\t\t\t});\n\t\t\t} else {\n\t\t\t\tctx.context.logger.warn(\n\t\t\t\t\t\"Could not extract assertion ID for replay protection\",\n\t\t\t\t\t{ providerId: provider.providerId },\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst attributes = extract.attributes || {};\n\t\t\tconst mapping = parsedSamlConfig.mapping ?? {};\n\n\t\t\tconst userInfo = {\n\t\t\t\t...Object.fromEntries(\n\t\t\t\t\tObject.entries(mapping.extraFields || {}).map(([key, value]) => [\n\t\t\t\t\t\tkey,\n\t\t\t\t\t\tattributes[value as string],\n\t\t\t\t\t]),\n\t\t\t\t),\n\t\t\t\tid: attributes[mapping.id || \"nameID\"] || extract.nameID,\n\t\t\t\temail: (\n\t\t\t\t\tattributes[mapping.email || \"email\"] || extract.nameID\n\t\t\t\t).toLowerCase(),\n\t\t\t\tname:\n\t\t\t\t\t[\n\t\t\t\t\t\tattributes[mapping.firstName || \"givenName\"],\n\t\t\t\t\t\tattributes[mapping.lastName || \"surname\"],\n\t\t\t\t\t]\n\t\t\t\t\t\t.filter(Boolean)\n\t\t\t\t\t\t.join(\" \") ||\n\t\t\t\t\tattributes[mapping.name || \"displayName\"] ||\n\t\t\t\t\textract.nameID,\n\t\t\t\temailVerified:\n\t\t\t\t\toptions?.trustEmailVerified && mapping.emailVerified\n\t\t\t\t\t\t? ((attributes[mapping.emailVerified] || false) as boolean)\n\t\t\t\t\t\t: false,\n\t\t\t};\n\t\t\tif (!userInfo.id || !userInfo.email) {\n\t\t\t\tctx.context.logger.error(\n\t\t\t\t\t\"Missing essential user info from SAML response\",\n\t\t\t\t\t{\n\t\t\t\t\t\tattributes: Object.keys(attributes),\n\t\t\t\t\t\tmapping,\n\t\t\t\t\t\textractedId: userInfo.id,\n\t\t\t\t\t\textractedEmail: userInfo.email,\n\t\t\t\t\t},\n\t\t\t\t);\n\t\t\t\tthrow new APIError(\"BAD_REQUEST\", {\n\t\t\t\t\tmessage: \"Unable to extract user ID or email from SAML response\",\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tconst isTrustedProvider: boolean =\n\t\t\t\t!!ctx.context.options.account?.accountLinking?.trustedProviders?.includes(\n\t\t\t\t\tprovider.providerId,\n\t\t\t\t) ||\n\t\t\t\t(\"domainVerified\" in provider &&\n\t\t\t\t\t!!(provider as { domainVerified?: boolean }).domainVerified &&\n\t\t\t\t\tvalidateEmailDomain(userInfo.email as string, provider.domain));\n\n\t\t\tconst callbackUrl =\n\t\t\t\trelayState?.callbackURL ||\n\t\t\t\tparsedSamlConfig.callbackUrl ||\n\t\t\t\tctx.context.baseURL;\n\n\t\t\tconst result = await handleOAuthUserInfo(ctx, {\n\t\t\t\tuserInfo: {\n\t\t\t\t\temail: userInfo.email as string,\n\t\t\t\t\tname: (userInfo.name || userInfo.email) as string,\n\t\t\t\t\tid: userInfo.id as string,\n\t\t\t\t\temailVerified: Boolean(userInfo.emailVerified),\n\t\t\t\t},\n\t\t\t\taccount: {\n\t\t\t\t\tproviderId: provider.providerId,\n\t\t\t\t\taccountId: userInfo.id as string,\n\t\t\t\t\taccessToken: \"\",\n\t\t\t\t\trefreshToken: \"\",\n\t\t\t\t},\n\t\t\t\tcallbackURL: callbackUrl,\n\t\t\t\tdisableSignUp: options?.disableImplicitSignUp,\n\t\t\t\tisTrustedProvider,\n\t\t\t});\n\n\t\t\tif (result.error) {\n\t\t\t\tthrow ctx.redirect(\n\t\t\t\t\t`${callbackUrl}?error=${result.error.split(\" \").join(\"_\")}`,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst { session, user } = result.data!;\n\n\t\t\tif (options?.provisionUser) {\n\t\t\t\tawait options.provisionUser({\n\t\t\t\t\tuser: user as User & Record<string, any>,\n\t\t\t\t\tuserInfo,\n\t\t\t\t\tprovider,\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tawait assignOrganizationFromProvider(ctx as any, {\n\t\t\t\tuser,\n\t\t\t\tprofile: {\n\t\t\t\t\tproviderType: \"saml\",\n\t\t\t\t\tproviderId: provider.providerId,\n\t\t\t\t\taccountId: userInfo.id as string,\n\t\t\t\t\temail: userInfo.email as string,\n\t\t\t\t\temailVerified: Boolean(userInfo.emailVerified),\n\t\t\t\t\trawAttributes: attributes,\n\t\t\t\t},\n\t\t\t\tprovider,\n\t\t\t\tprovisioningOptions: options?.organizationProvisioning,\n\t\t\t});\n\n\t\t\tawait setSessionCookie(ctx, { session, user });\n\n\t\t\tconst safeRedirectUrl = getSafeRedirectUrl(\n\t\t\t\trelayState?.callbackURL || parsedSamlConfig.callbackUrl,\n\t\t\t\tcurrentCallbackPath,\n\t\t\t\tappOrigin,\n\t\t\t\t(url, settings) => ctx.context.isTrustedOrigin(url, settings),\n\t\t\t);\n\t\t\tthrow ctx.redirect(safeRedirectUrl);\n\t\t},\n\t);\n};\n\nconst acsEndpointBodySchema = z.object({\n\tSAMLResponse: z.string(),\n\tRelayState: z.string().optional(),\n});\n\nexport const acsEndpoint = (options?: SSOOptions) => {\n\treturn createAuthEndpoint(\n\t\t\"/sso/saml2/sp/acs/:providerId\",\n\t\t{\n\t\t\tmethod: \"POST\",\n\t\t\tbody: acsEndpointBodySchema,\n\t\t\tmetadata: {\n\t\t\t\t...HIDE_METADATA,\n\t\t\t\tallowedMediaTypes: [\n\t\t\t\t\t\"application/x-www-form-urlencoded\",\n\t\t\t\t\t\"application/json\",\n\t\t\t\t],\n\t\t\t\topenapi: {\n\t\t\t\t\toperationId: \"handleSAMLAssertionConsumerService\",\n\t\t\t\t\tsummary: \"SAML Assertion Consumer Service\",\n\t\t\t\t\tdescription:\n\t\t\t\t\t\t\"Handles SAML responses from IdP after successful authentication\",\n\t\t\t\t\tresponses: {\n\t\t\t\t\t\t\"302\": {\n\t\t\t\t\t\t\tdescription:\n\t\t\t\t\t\t\t\t\"Redirects to the callback URL after successful authentication\",\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\tasync (ctx) => {\n\t\t\tconst { SAMLResponse, RelayState = \"\" } = ctx.body;\n\t\t\tconst { providerId } = ctx.params;\n\n\t\t\tconst maxResponseSize =\n\t\t\t\toptions?.saml?.maxResponseSize ?? DEFAULT_MAX_SAML_RESPONSE_SIZE;\n\t\t\tif (new TextEncoder().encode(SAMLResponse).length > maxResponseSize) {\n\t\t\t\tthrow new APIError(\"BAD_REQUEST\", {\n\t\t\t\t\tmessage: `SAML response exceeds maximum allowed size (${maxResponseSize} bytes)`,\n\t\t\t\t});\n\t\t\t}\n\n\t\t\t// If defaultSSO is configured, use it as the provider\n\t\t\tlet provider: SSOProvider<SSOOptions> | null = null;\n\n\t\t\tif (options?.defaultSSO?.length) {\n\t\t\t\t// For ACS endpoint, we can use the first default provider or try to match by providerId\n\t\t\t\tconst matchingDefault = providerId\n\t\t\t\t\t? options.defaultSSO.find(\n\t\t\t\t\t\t\t(defaultProvider) => defaultProvider.providerId === providerId,\n\t\t\t\t\t\t)\n\t\t\t\t\t: options.defaultSSO[0]; // Use first default provider if no specific providerId\n\n\t\t\t\tif (matchingDefault) {\n\t\t\t\t\tprovider = {\n\t\t\t\t\t\tissuer: matchingDefault.samlConfig?.issuer || \"\",\n\t\t\t\t\t\tproviderId: matchingDefault.providerId,\n\t\t\t\t\t\tuserId: \"default\",\n\t\t\t\t\t\tsamlConfig: matchingDefault.samlConfig,\n\t\t\t\t\t\tdomain: matchingDefault.domain,\n\t\t\t\t\t\t...(options.domainVerification?.enabled\n\t\t\t\t\t\t\t? { domainVerified: true }\n\t\t\t\t\t\t\t: {}),\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tprovider = await ctx.context.adapter\n\t\t\t\t\t.findOne<SSOProvider<SSOOptions>>({\n\t\t\t\t\t\tmodel: \"ssoProvider\",\n\t\t\t\t\t\twhere: [\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tfield: \"providerId\",\n\t\t\t\t\t\t\t\tvalue: providerId,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t],\n\t\t\t\t\t})\n\t\t\t\t\t.then((res) => {\n\t\t\t\t\t\tif (!res) return null;\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\t...res,\n\t\t\t\t\t\t\tsamlConfig: res.samlConfig\n\t\t\t\t\t\t\t\t? safeJsonParse<SAMLConfig>(\n\t\t\t\t\t\t\t\t\t\tres.samlConfig as unknown as string,\n\t\t\t\t\t\t\t\t\t) || undefined\n\t\t\t\t\t\t\t\t: undefined,\n\t\t\t\t\t\t};\n\t\t\t\t\t});\n\t\t\t}\n\n\t\t\tif (!provider?.samlConfig) {\n\t\t\t\tthrow new APIError(\"NOT_FOUND\", {\n\t\t\t\t\tmessage: \"No SAML provider found\",\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tif (\n\t\t\t\toptions?.domainVerification?.enabled &&\n\t\t\t\t!(\"domainVerified\" in provider && provider.domainVerified)\n\t\t\t) {\n\t\t\t\tthrow new APIError(\"UNAUTHORIZED\", {\n\t\t\t\t\tmessage: \"Provider domain has not been verified\",\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tconst parsedSamlConfig = provider.samlConfig;\n\t\t\t// Configure SP and IdP\n\t\t\tconst sp = saml.ServiceProvider({\n\t\t\t\tentityID:\n\t\t\t\t\tparsedSamlConfig.spMetadata?.entityID || parsedSamlConfig.issuer,\n\t\t\t\tassertionConsumerService: [\n\t\t\t\t\t{\n\t\t\t\t\t\tBinding: \"urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST\",\n\t\t\t\t\t\tLocation:\n\t\t\t\t\t\t\tparsedSamlConfig.callbackUrl ||\n\t\t\t\t\t\t\t`${ctx.context.baseURL}/sso/saml2/sp/acs/${providerId}`,\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t\twantMessageSigned: parsedSamlConfig.wantAssertionsSigned || false,\n\t\t\t\tmetadata: parsedSamlConfig.spMetadata?.metadata,\n\t\t\t\tprivateKey:\n\t\t\t\t\tparsedSamlConfig.spMetadata?.privateKey ||\n\t\t\t\t\tparsedSamlConfig.privateKey,\n\t\t\t\tprivateKeyPass: parsedSamlConfig.spMetadata?.privateKeyPass,\n\t\t\t\tnameIDFormat: parsedSamlConfig.identifierFormat\n\t\t\t\t\t? [parsedSamlConfig.identifierFormat]\n\t\t\t\t\t: undefined,\n\t\t\t});\n\n\t\t\t// Update where we construct the IdP\n\t\t\tconst idpData = parsedSamlConfig.idpMetadata;\n\t\t\tconst idp = !idpData?.metadata\n\t\t\t\t? saml.IdentityProvider({\n\t\t\t\t\t\tentityID: idpData?.entityID || parsedSamlConfig.issuer,\n\t\t\t\t\t\tsingleSignOnService: idpData?.singleSignOnService || [\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tBinding: \"urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect\",\n\t\t\t\t\t\t\t\tLocation: parsedSamlConfig.entryPoint,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t],\n\t\t\t\t\t\tsigningCert: idpData?.cert || parsedSamlConfig.cert,\n\t\t\t\t\t})\n\t\t\t\t: saml.IdentityProvider({\n\t\t\t\t\t\tmetadata: idpData.metadata,\n\t\t\t\t\t});\n\n\t\t\ttry {\n\t\t\t\tvalidateSingleAssertion(SAMLResponse);\n\t\t\t} catch (error) {\n\t\t\t\tif (error instanceof APIError) {\n\t\t\t\t\tconst redirectUrl =\n\t\t\t\t\t\tRelayState || parsedSamlConfig.callbackUrl || ctx.context.baseURL;\n\t\t\t\t\tconst errorCode =\n\t\t\t\t\t\terror.body?.code === \"SAML_MULTIPLE_ASSERTIONS\"\n\t\t\t\t\t\t\t? \"multiple_assertions\"\n\t\t\t\t\t\t\t: \"no_assertion\";\n\t\t\t\t\tthrow ctx.redirect(\n\t\t\t\t\t\t`${redirectUrl}?error=${errorCode}&error_description=${encodeURIComponent(error.message)}`,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tthrow error;\n\t\t\t}\n\n\t\t\t// Parse and validate SAML response\n\t\t\tlet parsedResponse: FlowResult;\n\t\t\ttry {\n\t\t\t\tparsedResponse = await sp.parseLoginResponse(idp, \"post\", {\n\t\t\t\t\tbody: {\n\t\t\t\t\t\tSAMLResponse,\n\t\t\t\t\t\tRelayState: RelayState || undefined,\n\t\t\t\t\t},\n\t\t\t\t});\n\n\t\t\t\tif (!parsedResponse?.extract) {\n\t\t\t\t\tthrow new Error(\"Invalid SAML response structure\");\n\t\t\t\t}\n\t\t\t} catch (error) {\n\t\t\t\tctx.context.logger.error(\"SAML response validation failed\", {\n\t\t\t\t\terror,\n\t\t\t\t\tdecodedResponse: Buffer.from(SAMLResponse, \"base64\").toString(\n\t\t\t\t\t\t\"utf-8\",\n\t\t\t\t\t),\n\t\t\t\t});\n\t\t\t\tthrow new APIError(\"BAD_REQUEST\", {\n\t\t\t\t\tmessage: \"Invalid SAML response\",\n\t\t\t\t\tdetails: error instanceof Error ? error.message : String(error),\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tconst { extract } = parsedResponse!;\n\n\t\t\tvalidateSAMLAlgorithms(parsedResponse, options?.saml?.algorithms);\n\n\t\t\tvalidateSAMLTimestamp((extract as any).conditions, {\n\t\t\t\tclockSkew: options?.saml?.clockSkew,\n\t\t\t\trequireTimestamps: options?.saml?.requireTimestamps,\n\t\t\t\tlogger: ctx.context.logger,\n\t\t\t});\n\n\t\t\tconst inResponseToAcs = (extract as any).inResponseTo as\n\t\t\t\t| string\n\t\t\t\t| undefined;\n\t\t\tconst shouldValidateInResponseToAcs =\n\t\t\t\toptions?.saml?.enableInResponseToValidation;\n\n\t\t\tif (shouldValidateInResponseToAcs) {\n\t\t\t\tconst allowIdpInitiated = options?.saml?.allowIdpInitiated !== false;\n\n\t\t\t\tif (inResponseToAcs) {\n\t\t\t\t\tlet storedRequest: AuthnRequestRecord | null = null;\n\n\t\t\t\t\tconst verification =\n\t\t\t\t\t\tawait ctx.context.internalAdapter.findVerificationValue(\n\t\t\t\t\t\t\t`${AUTHN_REQUEST_KEY_PREFIX}${inResponseToAcs}`,\n\t\t\t\t\t\t);\n\t\t\t\t\tif (verification) {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tstoredRequest = JSON.parse(\n\t\t\t\t\t\t\t\tverification.value,\n\t\t\t\t\t\t\t) as AuthnRequestRecord;\n\t\t\t\t\t\t\tif (storedRequest && storedRequest.expiresAt < Date.now()) {\n\t\t\t\t\t\t\t\tstoredRequest = null;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} catch {\n\t\t\t\t\t\t\tstoredRequest = null;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tif (!storedRequest) {\n\t\t\t\t\t\tctx.context.logger.error(\n\t\t\t\t\t\t\t\"SAML InResponseTo validation failed: unknown or expired request ID\",\n\t\t\t\t\t\t\t{ inResponseTo: inResponseToAcs, providerId },\n\t\t\t\t\t\t);\n\t\t\t\t\t\tconst redirectUrl =\n\t\t\t\t\t\t\tRelayState || parsedSamlConfig.callbackUrl || ctx.context.baseURL;\n\t\t\t\t\t\tthrow ctx.redirect(\n\t\t\t\t\t\t\t`${redirectUrl}?error=invalid_saml_response&error_description=Unknown+or+expired+request+ID`,\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\n\t\t\t\t\tif (storedRequest.providerId !== providerId) {\n\t\t\t\t\t\tctx.context.logger.error(\n\t\t\t\t\t\t\t\"SAML InResponseTo validation failed: provider mismatch\",\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tinResponseTo: inResponseToAcs,\n\t\t\t\t\t\t\t\texpectedProvider: storedRequest.providerId,\n\t\t\t\t\t\t\t\tactualProvider: providerId,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t);\n\t\t\t\t\t\tawait ctx.context.internalAdapter.deleteVerificationByIdentifier(\n\t\t\t\t\t\t\t`${AUTHN_REQUEST_KEY_PREFIX}${inResponseToAcs}`,\n\t\t\t\t\t\t);\n\t\t\t\t\t\tconst redirectUrl =\n\t\t\t\t\t\t\tRelayState || parsedSamlConfig.callbackUrl || ctx.context.baseURL;\n\t\t\t\t\t\tthrow ctx.redirect(\n\t\t\t\t\t\t\t`${redirectUrl}?error=invalid_saml_response&error_description=Provider+mismatch`,\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\n\t\t\t\t\tawait ctx.context.internalAdapter.deleteVerificationByIdentifier(\n\t\t\t\t\t\t`${AUTHN_REQUEST_KEY_PREFIX}${inResponseToAcs}`,\n\t\t\t\t\t);\n\t\t\t\t} else if (!allowIdpInitiated) {\n\t\t\t\t\tctx.context.logger.error(\n\t\t\t\t\t\t\"SAML IdP-initiated SSO rejected: InResponseTo missing and allowIdpInitiated is false\",\n\t\t\t\t\t\t{ providerId },\n\t\t\t\t\t);\n\t\t\t\t\tconst redirectUrl =\n\t\t\t\t\t\tRelayState || parsedSamlConfig.callbackUrl || ctx.context.baseURL;\n\t\t\t\t\tthrow ctx.redirect(\n\t\t\t\t\t\t`${redirectUrl}?error=unsolicited_response&error_description=IdP-initiated+SSO+not+allowed`,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Assertion Replay Protection\n\t\t\tconst samlContentAcs = Buffer.from(SAMLResponse, \"base64\").toString(\n\t\t\t\t\"utf-8\",\n\t\t\t);\n\t\t\tconst assertionIdAcs = extractAssertionId(samlContentAcs);\n\n\t\t\tif (assertionIdAcs) {\n\t\t\t\tconst issuer = idp.entityMeta.getEntityID();\n\t\t\t\tconst conditions = (extract as any).conditions as\n\t\t\t\t\t| SAMLConditions\n\t\t\t\t\t| undefined;\n\t\t\t\tconst clockSkew = options?.saml?.clockSkew ?? DEFAULT_CLOCK_SKEW_MS;\n\t\t\t\tconst expiresAt = conditions?.notOnOrAfter\n\t\t\t\t\t? new Date(conditions.notOnOrAfter).getTime() + clockSkew\n\t\t\t\t\t: Date.now() + DEFAULT_ASSERTION_TTL_MS;\n\n\t\t\t\tconst existingAssertion =\n\t\t\t\t\tawait ctx.context.internalAdapter.findVerificationValue(\n\t\t\t\t\t\t`${USED_ASSERTION_KEY_PREFIX}${assertionIdAcs}`,\n\t\t\t\t\t);\n\n\t\t\t\tlet isReplay = false;\n\t\t\t\tif (existingAssertion) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst stored = JSON.parse(existingAssertion.value);\n\t\t\t\t\t\tif (stored.expiresAt >= Date.now()) {\n\t\t\t\t\t\t\tisReplay = true;\n\t\t\t\t\t\t}\n\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\tctx.context.logger.warn(\"Failed to parse stored assertion record\", {\n\t\t\t\t\t\t\tassertionId: assertionIdAcs,\n\t\t\t\t\t\t\terror,\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (isReplay) {\n\t\t\t\t\tctx.context.logger.error(\n\t\t\t\t\t\t\"SAML assertion replay detected: assertion ID already used\",\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tassertionId: assertionIdAcs,\n\t\t\t\t\t\t\tissuer,\n\t\t\t\t\t\t\tproviderId,\n\t\t\t\t\t\t},\n\t\t\t\t\t);\n\t\t\t\t\tconst redirectUrl =\n\t\t\t\t\t\tRelayState || parsedSamlConfig.callbackUrl || ctx.context.baseURL;\n\t\t\t\t\tthrow ctx.redirect(\n\t\t\t\t\t\t`${redirectUrl}?error=replay_detected&error_description=SAML+assertion+has+already+been+used`,\n\t\t\t\t\t);\n\t\t\t\t}\n\n\t\t\t\tawait ctx.context.internalAdapter.createVerificationValue({\n\t\t\t\t\tidentifier: `${USED_ASSERTION_KEY_PREFIX}${assertionIdAcs}`,\n\t\t\t\t\tvalue: JSON.stringify({\n\t\t\t\t\t\tassertionId: assertionIdAcs,\n\t\t\t\t\t\tissuer,\n\t\t\t\t\t\tproviderId,\n\t\t\t\t\t\tusedAt: Date.now(),\n\t\t\t\t\t\texpiresAt,\n\t\t\t\t\t}),\n\t\t\t\t\texpiresAt: new Date(expiresAt),\n\t\t\t\t});\n\t\t\t} else {\n\t\t\t\tctx.context.logger.warn(\n\t\t\t\t\t\"Could not extract assertion ID for replay protection\",\n\t\t\t\t\t{ providerId },\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst attributes = extract.attributes || {};\n\t\t\tconst mapping = parsedSamlConfig.mapping ?? {};\n\n\t\t\tconst userInfo = {\n\t\t\t\t...Object.fromEntries(\n\t\t\t\t\tObject.entries(mapping.extraFields || {}).map(([key, value]) => [\n\t\t\t\t\t\tkey,\n\t\t\t\t\t\tattributes[value as string],\n\t\t\t\t\t]),\n\t\t\t\t),\n\t\t\t\tid: attributes[mapping.id || \"nameID\"] || extract.nameID,\n\t\t\t\temail: (\n\t\t\t\t\tattributes[mapping.email || \"email\"] || extract.nameID\n\t\t\t\t).toLowerCase(),\n\t\t\t\tname:\n\t\t\t\t\t[\n\t\t\t\t\t\tattributes[mapping.firstName || \"givenName\"],\n\t\t\t\t\t\tattributes[mapping.lastName || \"surname\"],\n\t\t\t\t\t]\n\t\t\t\t\t\t.filter(Boolean)\n\t\t\t\t\t\t.join(\" \") ||\n\t\t\t\t\tattributes[mapping.name || \"displayName\"] ||\n\t\t\t\t\textract.nameID,\n\t\t\t\temailVerified:\n\t\t\t\t\toptions?.trustEmailVerified && mapping.emailVerified\n\t\t\t\t\t\t? ((attributes[mapping.emailVerified] || false) as boolean)\n\t\t\t\t\t\t: false,\n\t\t\t};\n\n\t\t\tif (!userInfo.id || !userInfo.email) {\n\t\t\t\tctx.context.logger.error(\n\t\t\t\t\t\"Missing essential user info from SAML response\",\n\t\t\t\t\t{\n\t\t\t\t\t\tattributes: Object.keys(attributes),\n\t\t\t\t\t\tmapping,\n\t\t\t\t\t\textractedId: userInfo.id,\n\t\t\t\t\t\textractedEmail: userInfo.email,\n\t\t\t\t\t},\n\t\t\t\t);\n\t\t\t\tthrow new APIError(\"BAD_REQUEST\", {\n\t\t\t\t\tmessage: \"Unable to extract user ID or email from SAML response\",\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tconst isTrustedProvider: boolean =\n\t\t\t\t!!ctx.context.options.account?.accountLinking?.trustedProviders?.includes(\n\t\t\t\t\tprovider.providerId,\n\t\t\t\t) ||\n\t\t\t\t(\"domainVerified\" in provider &&\n\t\t\t\t\t!!(provider as { domainVerified?: boolean }).domainVerified &&\n\t\t\t\t\tvalidateEmailDomain(userInfo.email as string, provider.domain));\n\n\t\t\tconst callbackUrl =\n\t\t\t\tRelayState || parsedSamlConfig.callbackUrl || ctx.context.baseURL;\n\n\t\t\tconst result = await handleOAuthUserInfo(ctx, {\n\t\t\t\tuserInfo: {\n\t\t\t\t\temail: userInfo.email as string,\n\t\t\t\t\tname: (userInfo.name || userInfo.email) as string,\n\t\t\t\t\tid: userInfo.id as string,\n\t\t\t\t\temailVerified: Boolean(userInfo.emailVerified),\n\t\t\t\t},\n\t\t\t\taccount: {\n\t\t\t\t\tproviderId: provider.providerId,\n\t\t\t\t\taccountId: userInfo.id as string,\n\t\t\t\t\taccessToken: \"\",\n\t\t\t\t\trefreshToken: \"\",\n\t\t\t\t},\n\t\t\t\tcallbackURL: callbackUrl,\n\t\t\t\tdisableSignUp: options?.disableImplicitSignUp,\n\t\t\t\tisTrustedProvider,\n\t\t\t});\n\n\t\t\tif (result.error) {\n\t\t\t\tthrow ctx.redirect(\n\t\t\t\t\t`${callbackUrl}?error=${result.error.split(\" \").join(\"_\")}`,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst { session, user } = result.data!;\n\n\t\t\tif (options?.provisionUser) {\n\t\t\t\tawait options.provisionUser({\n\t\t\t\t\tuser: user as User & Record<string, any>,\n\t\t\t\t\tuserInfo,\n\t\t\t\t\tprovider,\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tawait assignOrganizationFromProvider(ctx as any, {\n\t\t\t\tuser,\n\t\t\t\tprofile: {\n\t\t\t\t\tproviderType: \"saml\",\n\t\t\t\t\tproviderId: provider.providerId,\n\t\t\t\t\taccountId: userInfo.id as string,\n\t\t\t\t\temail: userInfo.email as string,\n\t\t\t\t\temailVerified: Boolean(userInfo.emailVerified),\n\t\t\t\t\trawAttributes: attributes,\n\t\t\t\t},\n\t\t\t\tprovider,\n\t\t\t\tprovisioningOptions: options?.organizationProvisioning,\n\t\t\t});\n\n\t\t\tawait setSessionCookie(ctx, { session, user });\n\t\t\tthrow ctx.redirect(callbackUrl);\n\t\t},\n\t);\n};\n","import type { BetterAuthPlugin } from \"better-auth\";\nimport { createAuthMiddleware } from \"better-auth/api\";\nimport { XMLValidator } from \"fast-xml-parser\";\nimport * as saml from \"samlify\";\nimport { assignOrganizationByDomain } from \"./linking\";\nimport {\n\trequestDomainVerification,\n\tverifyDomain,\n} from \"./routes/domain-verification\";\nimport {\n\tdeleteSSOProvider,\n\tgetSSOProvider,\n\tlistSSOProviders,\n\tupdateSSOProvider,\n} from \"./routes/providers\";\nimport {\n\tacsEndpoint,\n\tcallbackSSO,\n\tcallbackSSOSAML,\n\tregisterSSOProvider,\n\tsignInSSO,\n\tspMetadata,\n} from \"./routes/sso\";\n\nexport {\n\tDEFAULT_CLOCK_SKEW_MS,\n\tDEFAULT_MAX_SAML_METADATA_SIZE,\n\tDEFAULT_MAX_SAML_RESPONSE_SIZE,\n} from \"./constants\";\n\nexport {\n\ttype SAMLConditions,\n\ttype TimestampValidationOptions,\n\tvalidateSAMLTimestamp,\n} from \"./routes/sso\";\n\nexport {\n\ttype AlgorithmValidationOptions,\n\tDataEncryptionAlgorithm,\n\ttype DeprecatedAlgorithmBehavior,\n\tDigestAlgorithm,\n\tKeyEncryptionAlgorithm,\n\tSignatureAlgorithm,\n} from \"./saml\";\n\nimport type { OIDCConfig, SAMLConfig, SSOOptions, SSOProvider } from \"./types\";\n\nexport type { SAMLConfig, OIDCConfig, SSOOptions, SSOProvider };\n\ndeclare module \"@better-auth/core\" {\n\tinterface BetterAuthPluginRegistry<AuthOptions, Options> {\n\t\tsso: {\n\t\t\tcreator: typeof sso;\n\t\t};\n\t}\n}\n\nexport {\n\tcomputeDiscoveryUrl,\n\ttype DiscoverOIDCConfigParams,\n\tDiscoveryError,\n\ttype DiscoveryErrorCode,\n\tdiscoverOIDCConfig,\n\tfetchDiscoveryDocument,\n\ttype HydratedOIDCConfig,\n\tneedsRuntimeDiscovery,\n\tnormalizeDiscoveryUrls,\n\tnormalizeUrl,\n\ttype OIDCDiscoveryDocument,\n\tREQUIRED_DISCOVERY_FIELDS,\n\ttype RequiredDiscoveryField,\n\tselectTokenEndpointAuthMethod,\n\tvalidateDiscoveryDocument,\n\tvalidateDiscoveryUrl,\n} from \"./oidc\";\n\nconst fastValidator = {\n\tasync validate(xml: string) {\n\t\tconst isValid = XMLValidator.validate(xml, {\n\t\t\tallowBooleanAttributes: true,\n\t\t});\n\t\tif (isValid === true) return \"SUCCESS_VALIDATE_XML\";\n\t\tthrow \"ERR_INVALID_XML\";\n\t},\n};\n\nsaml.setSchemaValidator(fastValidator);\n\ntype DomainVerificationEndpoints = {\n\trequestDomainVerification: ReturnType<typeof requestDomainVerification>;\n\tverifyDomain: ReturnType<typeof verifyDomain>;\n};\n\ntype SSOEndpoints<O extends SSOOptions> = {\n\tspMetadata: ReturnType<typeof spMetadata>;\n\tregisterSSOProvider: ReturnType<typeof registerSSOProvider<O>>;\n\tsignInSSO: ReturnType<typeof signInSSO>;\n\tcallbackSSO: ReturnType<typeof callbackSSO>;\n\tcallbackSSOSAML: ReturnType<typeof callbackSSOSAML>;\n\tacsEndpoint: ReturnType<typeof acsEndpoint>;\n\tlistSSOProviders: ReturnType<typeof listSSOProviders>;\n\tgetSSOProvider: ReturnType<typeof getSSOProvider>;\n\tupdateSSOProvider: ReturnType<typeof updateSSOProvider>;\n\tdeleteSSOProvider: ReturnType<typeof deleteSSOProvider>;\n};\n\nexport type SSOPlugin<O extends SSOOptions> = {\n\tid: \"sso\";\n\tendpoints: SSOEndpoints<O> &\n\t\t(O extends { domainVerification: { enabled: true } }\n\t\t\t? DomainVerificationEndpoints\n\t\t\t: {});\n};\n\n/**\n * SAML endpoint paths that should skip origin check validation.\n * These endpoints receive POST requests from external Identity Providers,\n * which won't have a matching Origin header.\n */\nconst SAML_SKIP_ORIGIN_CHECK_PATHS = [\n\t\"/sso/saml2/callback\", // SP-initiated SSO callback (prefix matches /callback/:providerId)\n\t\"/sso/saml2/sp/acs\", // IdP-initiated SSO ACS (prefix matches /sp/acs/:providerId)\n];\n\nexport function sso<\n\tO extends SSOOptions & {\n\t\tdomainVerification?: { enabled: true };\n\t},\n>(\n\toptions?: O | undefined,\n): {\n\tid: \"sso\";\n\tendpoints: SSOEndpoints<O> & DomainVerificationEndpoints;\n\tschema: NonNullable<BetterAuthPlugin[\"schema\"]>;\n\toptions: O;\n};\nexport function sso<O extends SSOOptions>(\n\toptions?: O | undefined,\n): {\n\tid: \"sso\";\n\tendpoints: SSOEndpoints<O>;\n};\n\nexport function sso<O extends SSOOptions>(\n\toptions?: O | undefined,\n): BetterAuthPlugin {\n\tconst optionsWithStore = options as O;\n\n\tlet endpoints = {\n\t\tspMetadata: spMetadata(),\n\t\tregisterSSOProvider: registerSSOProvider(optionsWithStore),\n\t\tsignInSSO: signInSSO(optionsWithStore),\n\t\tcallbackSSO: callbackSSO(optionsWithStore),\n\t\tcallbackSSOSAML: callbackSSOSAML(optionsWithStore),\n\t\tacsEndpoint: acsEndpoint(optionsWithStore),\n\t\tlistSSOProviders: listSSOProviders(),\n\t\tgetSSOProvider: getSSOProvider(),\n\t\tupdateSSOProvider: updateSSOProvider(optionsWithStore),\n\t\tdeleteSSOProvider: deleteSSOProvider(),\n\t};\n\n\tif (options?.domainVerification?.enabled) {\n\t\tconst domainVerificationEndpoints = {\n\t\t\trequestDomainVerification: requestDomainVerification(optionsWithStore),\n\t\t\tverifyDomain: verifyDomain(optionsWithStore),\n\t\t};\n\n\t\tendpoints = {\n\t\t\t...endpoints,\n\t\t\t...domainVerificationEndpoints,\n\t\t};\n\t}\n\n\treturn {\n\t\tid: \"sso\",\n\t\tinit(ctx) {\n\t\t\tconst existing = ctx.skipOriginCheck;\n\t\t\tif (existing === true) {\n\t\t\t\treturn {};\n\t\t\t}\n\t\t\tconst existingPaths = Array.isArray(existing) ? existing : [];\n\t\t\treturn {\n\t\t\t\tcontext: {\n\t\t\t\t\tskipOriginCheck: [...existingPaths, ...SAML_SKIP_ORIGIN_CHECK_PATHS],\n\t\t\t\t},\n\t\t\t};\n\t\t},\n\t\tendpoints,\n\t\thooks: {\n\t\t\tafter: [\n\t\t\t\t{\n\t\t\t\t\tmatcher(context) {\n\t\t\t\t\t\treturn context.path?.startsWith(\"/callback/\") ?? false;\n\t\t\t\t\t},\n\t\t\t\t\thandler: createAuthMiddleware(async (ctx) => {\n\t\t\t\t\t\tconst newSession = ctx.context.newSession;\n\t\t\t\t\t\tif (!newSession?.user) {\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (!ctx.context.hasPlugin(\"organization\")) {\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tawait assignOrganizationByDomain(ctx, {\n\t\t\t\t\t\t\tuser: newSession.user,\n\t\t\t\t\t\t\tprovisioningOptions: options?.organizationProvisioning,\n\t\t\t\t\t\t\tdomainVerification: options?.domainVerification,\n\t\t\t\t\t\t});\n\t\t\t\t\t}),\n\t\t\t\t},\n\t\t\t],\n\t\t},\n\t\tschema: {\n\t\t\tssoProvider: {\n\t\t\t\tmodelName: options?.modelName ?? \"ssoProvider\",\n\t\t\t\tfields: {\n\t\t\t\t\tissuer: {\n\t\t\t\t\t\ttype: \"string\",\n\t\t\t\t\t\trequired: true,\n\t\t\t\t\t\tfieldName: options?.fields?.issuer ?? \"issuer\",\n\t\t\t\t\t},\n\t\t\t\t\toidcConfig: {\n\t\t\t\t\t\ttype: \"string\",\n\t\t\t\t\t\trequired: false,\n\t\t\t\t\t\tfieldName: options?.fields?.oidcConfig ?? \"oidcConfig\",\n\t\t\t\t\t},\n\t\t\t\t\tsamlConfig: {\n\t\t\t\t\t\ttype: \"string\",\n\t\t\t\t\t\trequired: false,\n\t\t\t\t\t\tfieldName: options?.fields?.samlConfig ?? \"samlConfig\",\n\t\t\t\t\t},\n\t\t\t\t\tuserId: {\n\t\t\t\t\t\ttype: \"string\",\n\t\t\t\t\t\treferences: {\n\t\t\t\t\t\t\tmodel: \"user\",\n\t\t\t\t\t\t\tfield: \"id\",\n\t\t\t\t\t\t},\n\t\t\t\t\t\tfieldName: options?.fields?.userId ?? \"userId\",\n\t\t\t\t\t},\n\t\t\t\t\tproviderId: {\n\t\t\t\t\t\ttype: \"string\",\n\t\t\t\t\t\trequired: true,\n\t\t\t\t\t\tunique: true,\n\t\t\t\t\t\tfieldName: options?.fields?.providerId ?? \"providerId\",\n\t\t\t\t\t},\n\t\t\t\t\torganizationId: {\n\t\t\t\t\t\ttype: \"string\",\n\t\t\t\t\t\trequired: false,\n\t\t\t\t\t\tfieldName: options?.fields?.organizationId ?? \"organizationId\",\n\t\t\t\t\t},\n\t\t\t\t\tdomain: {\n\t\t\t\t\t\ttype: \"string\",\n\t\t\t\t\t\trequired: true,\n\t\t\t\t\t\tfieldName: options?.fields?.domain ?? \"domain\",\n\t\t\t\t\t},\n\t\t\t\t\t...(options?.domainVerification?.enabled\n\t\t\t\t\t\t? { domainVerified: { type: \"boolean\", required: false } }\n\t\t\t\t\t\t: {}),\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\toptions: options as NoInfer<O>,\n\t} satisfies BetterAuthPlugin;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;AAWA,SAAgB,cACf,OACW;AACX,KAAI,CAAC,MAAO,QAAO;AAEnB,KAAI,OAAO,UAAU,SACpB,QAAO;AAGR,KAAI,OAAO,UAAU,SACpB,KAAI;AACH,SAAO,KAAK,MAAM,MAAM;UAChB,OAAO;AACf,QAAM,IAAI,MACT,yBAAyB,iBAAiB,QAAQ,MAAM,UAAU,kBAClE;;AAIH,QAAO;;;;;AAMR,MAAa,iBAAiB,cAAsB,eAAuB;CAC1E,MAAM,SAAS,aAAa,aAAa;AAKzC,QAJgB,WACd,MAAM,IAAI,CACV,KAAK,MAAM,EAAE,MAAM,CAAC,aAAa,CAAC,CAClC,OAAO,QAAQ,CACF,MAAM,MAAM,WAAW,KAAK,OAAO,SAAS,IAAI,IAAI,CAAC;;;;;;AAOrE,MAAa,uBAAuB,OAAe,WAAmB;CACrE,MAAM,cAAc,MAAM,MAAM,IAAI,CAAC,IAAI,aAAa;AACtD,KAAI,CAAC,eAAe,CAAC,OACpB,QAAO;AAER,QAAO,cAAc,aAAa,OAAO;;AAG1C,SAAgB,iBAAiB,SAAiB;CAOjD,MAAM,OAAO,IAAI,gBAJE,QAAQ,SAAS,aAAa,GAC9C,UACA,gCAAgC,QAAQ,6BAEC;AAE5C,QAAO;EACN,mBAAmB,KAAK;EACxB,WAAW,KAAK;EAChB,UAAU,KAAK;EACf,oBACC,KAAK,UAAU,mBAAmB,aAAa,IAAI;EACpD;;AAGF,SAAgB,aAAa,UAA0B;AACtD,KAAI,SAAS,UAAU,EACtB,QAAO;AAER,QAAO,OAAO,SAAS,MAAM,GAAG;;;;;;;;;ACnDjC,eAAsB,+BACrB,KACA,SACgB;CAChB,MAAM,EAAE,MAAM,SAAS,UAAU,OAAO,wBAAwB;AAEhE,KAAI,CAAC,SAAS,eACb;AAGD,KAAI,qBAAqB,SACxB;AAGD,KAAI,CAAC,IAAI,QAAQ,UAAU,eAAe,CACzC;AAWD,KARwB,MAAM,IAAI,QAAQ,QAAQ,QAAQ;EACzD,OAAO;EACP,OAAO,CACN;GAAE,OAAO;GAAkB,OAAO,SAAS;GAAgB,EAC3D;GAAE,OAAO;GAAU,OAAO,KAAK;GAAI,CACnC;EACD,CAAC,CAGD;CAGD,MAAM,OAAO,qBAAqB,UAC/B,MAAM,oBAAoB,QAAQ;EAClC;EACA,UAAU,QAAQ,iBAAiB,EAAE;EACrC;EACA;EACA,CAAC,GACD,qBAAqB,eAAe;AAEvC,OAAM,IAAI,QAAQ,QAAQ,OAAO;EAChC,OAAO;EACP,MAAM;GACL,gBAAgB,SAAS;GACzB,QAAQ,KAAK;GACb;GACA,2BAAW,IAAI,MAAM;GACrB;EACD,CAAC;;;;;;;;;;AAmBH,eAAsB,2BACrB,KACA,SACgB;CAChB,MAAM,EAAE,MAAM,qBAAqB,uBAAuB;AAE1D,KAAI,qBAAqB,SACxB;AAGD,KAAI,CAAC,IAAI,QAAQ,UAAU,eAAe,CACzC;CAGD,MAAM,SAAS,KAAK,MAAM,MAAM,IAAI,CAAC;AACrC,KAAI,CAAC,OACJ;CAKD,MAAM,cAA4D,CACjE;EAAE,OAAO;EAAU,OAAO;EAAQ,CAClC;AAED,KAAI,oBAAoB,QACvB,aAAY,KAAK;EAAE,OAAO;EAAkB,OAAO;EAAM,CAAC;CAG3D,IAAI,cAAc,MAAM,IAAI,QAAQ,QAAQ,QAAiC;EAC5E,OAAO;EACP,OAAO;EACP,CAAC;AAGF,KAAI,CAAC,YASJ,gBARqB,MAAM,IAAI,QAAQ,QAAQ,SAE7C;EACD,OAAO;EACP,OAAO,oBAAoB,UACxB,CAAC;GAAE,OAAO;GAAkB,OAAO;GAAM,CAAC,GAC1C,EAAE;EACL,CAAC,EAEY,MAAM,MAAM,cAAc,QAAQ,EAAE,OAAO,CAAC,IAAI;AAG/D,KAAI,CAAC,eAAe,CAAC,YAAY,eAChC;AAWD,KARwB,MAAM,IAAI,QAAQ,QAAQ,QAAQ;EACzD,OAAO;EACP,OAAO,CACN;GAAE,OAAO;GAAkB,OAAO,YAAY;GAAgB,EAC9D;GAAE,OAAO;GAAU,OAAO,KAAK;GAAI,CACnC;EACD,CAAC,CAGD;CAGD,MAAM,OAAO,qBAAqB,UAC/B,MAAM,oBAAoB,QAAQ;EAClC;EACA,UAAU,EAAE;EACZ,UAAU;EACV,CAAC,GACD,qBAAqB,eAAe;AAEvC,OAAM,IAAI,QAAQ,QAAQ,OAAO;EAChC,OAAO;EACP,MAAM;GACL,gBAAgB,YAAY;GAC5B,QAAQ,KAAK;GACb;GACA,2BAAW,IAAI,MAAM;GACrB;EACD,CAAC;;;;;ACpKH,MAAM,+BAA+BA,IAAE,OAAO,EAC7C,YAAYA,IAAE,QAAQ,EACtB,CAAC;AAEF,MAAa,6BAA6B,YAAwB;AACjE,QAAO,mBACN,oCACA;EACC,QAAQ;EACR,MAAM;EACN,UAAU,EACT,SAAS;GACR,SAAS;GACT,aACC;GACD,WAAW;IACV,OAAO,EACN,aAAa,sBACb;IACD,OAAO,EACN,aAAa,oCACb;IACD,OAAO,EACN,aAAa,qCACb;IACD;GACD,EACD;EACD,KAAK,CAAC,kBAAkB;EACxB,EACD,OAAO,QAAQ;EACd,MAAM,OAAO,IAAI;EACjB,MAAM,WAAW,MAAM,IAAI,QAAQ,QAAQ,QAEzC;GACD,OAAO;GACP,OAAO,CAAC;IAAE,OAAO;IAAc,OAAO,KAAK;IAAY,CAAC;GACxD,CAAC;AAEF,MAAI,CAAC,SACJ,OAAM,IAAI,SAAS,aAAa;GAC/B,SAAS;GACT,MAAM;GACN,CAAC;EAGH,MAAM,SAAS,IAAI,QAAQ,QAAQ,KAAK;EACxC,IAAI,cAAc;AAClB,MAAI,SAAS,eASZ,eARyB,MAAM,IAAI,QAAQ,QAAQ,MAAM;GACxD,OAAO;GACP,OAAO,CACN;IAAE,OAAO;IAAU,OAAO;IAAQ,EAClC;IAAE,OAAO;IAAkB,OAAO,SAAS;IAAgB,CAC3D;GACD,CAAC,GAE+B;AAGlC,MAAI,SAAS,WAAW,UAAU,CAAC,YAClC,OAAM,IAAI,SAAS,aAAa;GAC/B,SACC;GACD,MAAM;GACN,CAAC;AAGH,MAAI,oBAAoB,YAAY,SAAS,eAC5C,OAAM,IAAI,SAAS,YAAY;GAC9B,SAAS;GACT,MAAM;GACN,CAAC;EAGH,MAAM,qBACL,MAAM,IAAI,QAAQ,QAAQ,QAAsB;GAC/C,OAAO;GACP,OAAO,CACN;IACC,OAAO;IACP,OAAO,QAAQ,oBAAoB,cAChC,GAAG,QAAQ,oBAAoB,YAAY,GAAG,SAAS,eACvD,qBAAqB,SAAS;IACjC,EACD;IAAE,OAAO;IAAa,uBAAO,IAAI,MAAM;IAAE,UAAU;IAAM,CACzD;GACD,CAAC;AAEH,MAAI,oBAAoB;AACvB,OAAI,UAAU,IAAI;AAClB,UAAO,IAAI,KAAK,EAAE,yBAAyB,mBAAmB,OAAO,CAAC;;EAGvE,MAAM,0BAA0B,qBAAqB,GAAG;AACxD,QAAM,IAAI,QAAQ,QAAQ,OAAqB;GAC9C,OAAO;GACP,MAAM;IACL,YAAY,QAAQ,oBAAoB,cACrC,GAAG,QAAQ,oBAAoB,YAAY,GAAG,SAAS,eACvD,qBAAqB,SAAS;IACjC,2BAAW,IAAI,MAAM;IACrB,2BAAW,IAAI,MAAM;IACrB,OAAO;IACP,WAAW,IAAI,KAAK,KAAK,KAAK,GAAG,OAAO,KAAK,IAAI,IAAK;IACtD;GACD,CAAC;AAEF,MAAI,UAAU,IAAI;AAClB,SAAO,IAAI,KAAK,EACf,yBACA,CAAC;GAEH;;AAGF,MAAa,gBAAgB,YAAwB;AACpD,QAAO,mBACN,sBACA;EACC,QAAQ;EACR,MAAM;EACN,UAAU,EACT,SAAS;GACR,SAAS;GACT,aAAa;GACb,WAAW;IACV,OAAO,EACN,aAAa,sBACb;IACD,OAAO,EACN,aACC,sEACD;IACD,OAAO,EACN,aACC,qEACD;IACD,OAAO,EACN,aAAa,iCACb;IACD;GACD,EACD;EACD,KAAK,CAAC,kBAAkB;EACxB,EACD,OAAO,QAAQ;EACd,MAAM,OAAO,IAAI;EACjB,MAAM,WAAW,MAAM,IAAI,QAAQ,QAAQ,QAEzC;GACD,OAAO;GACP,OAAO,CAAC;IAAE,OAAO;IAAc,OAAO,KAAK;IAAY,CAAC;GACxD,CAAC;AAEF,MAAI,CAAC,SACJ,OAAM,IAAI,SAAS,aAAa;GAC/B,SAAS;GACT,MAAM;GACN,CAAC;EAGH,MAAM,SAAS,IAAI,QAAQ,QAAQ,KAAK;EACxC,IAAI,cAAc;AAClB,MAAI,SAAS,eASZ,eARyB,MAAM,IAAI,QAAQ,QAAQ,MAAM;GACxD,OAAO;GACP,OAAO,CACN;IAAE,OAAO;IAAU,OAAO;IAAQ,EAClC;IAAE,OAAO;IAAkB,OAAO,SAAS;IAAgB,CAC3D;GACD,CAAC,GAE+B;AAGlC,MAAI,SAAS,WAAW,UAAU,CAAC,YAClC,OAAM,IAAI,SAAS,aAAa;GAC/B,SACC;GACD,MAAM;GACN,CAAC;AAGH,MAAI,oBAAoB,YAAY,SAAS,eAC5C,OAAM,IAAI,SAAS,YAAY;GAC9B,SAAS;GACT,MAAM;GACN,CAAC;EAGH,MAAM,qBACL,MAAM,IAAI,QAAQ,QAAQ,QAAsB;GAC/C,OAAO;GACP,OAAO,CACN;IACC,OAAO;IACP,OAAO,QAAQ,oBAAoB,cAChC,GAAG,QAAQ,oBAAoB,YAAY,GAAG,SAAS,eACvD,qBAAqB,SAAS;IACjC,EACD;IAAE,OAAO;IAAa,uBAAO,IAAI,MAAM;IAAE,UAAU;IAAM,CACzD;GACD,CAAC;AAEH,MAAI,CAAC,mBACJ,OAAM,IAAI,SAAS,aAAa;GAC/B,SAAS;GACT,MAAM;GACN,CAAC;EAGH,IAAI,UAAoB,EAAE;EAC1B,IAAI;AAEJ,MAAI;AACH,SAAM,MAAM,OAAO;WACX,OAAO;AACf,OAAI,QAAQ,OAAO,MAClB,4EACA,MACA;AACD,SAAM,IAAI,SAAS,yBAAyB;IAC3C,SAAS;IACT,MAAM;IACN,CAAC;;AAGH,MAAI;AAIH,cAHmB,MAAM,IAAI,WAC5B,IAAI,IAAI,SAAS,OAAO,CAAC,SACzB,EACoB,MAAM;WACnB,OAAO;AACf,OAAI,QAAQ,OAAO,KAClB,4DACA,MACA;;AAQF,MAAI,CALW,QAAQ,MAAM,WAC5B,OAAO,SACN,GAAG,mBAAmB,WAAW,GAAG,mBAAmB,QACvD,CACD,CAEA,OAAM,IAAI,SAAS,eAAe;GACjC,SAAS;GACT,MAAM;GACN,CAAC;AAGH,QAAM,IAAI,QAAQ,QAAQ,OAAgC;GACzD,OAAO;GACP,OAAO,CAAC;IAAE,OAAO;IAAc,OAAO,SAAS;IAAY,CAAC;GAC5D,QAAQ,EACP,gBAAgB,MAChB;GACD,CAAC;AAEF,MAAI,UAAU,IAAI;GAGnB;;;;;;;;;;;ACtQF,MAAa,2BAA2B;;AAGxC,MAAa,4BAA4B;;;;;AAUzC,MAAa,+BAA+B,MAAS;;;;;AAMrD,MAAa,2BAA2B,MAAU;;;;;;;;;;AAWlD,MAAa,wBAAwB,MAAS;;;;;AAU9C,MAAa,iCAAiC,MAAM;;;;;AAMpD,MAAa,iCAAiC,MAAM;;;;ACvDpD,MAAa,YAAY,IAAI,UAAU;CACtC,kBAAkB;CAClB,qBAAqB;CACrB,gBAAgB;CAChB,iBAAiB;CACjB,CAAC;AAEF,SAAgB,SAAS,KAAc,UAA2B;AACjE,KAAI,CAAC,OAAO,OAAO,QAAQ,SAAU,QAAO;CAE5C,MAAM,SAAS;AAEf,KAAI,YAAY,OACf,QAAO,OAAO;AAGf,MAAK,MAAM,SAAS,OAAO,OAAO,OAAO,CACxC,KAAI,MAAM,QAAQ,MAAM,CACvB,MAAK,MAAM,QAAQ,OAAO;EACzB,MAAM,QAAQ,SAAS,MAAM,SAAS;AACtC,MAAI,MAAO,QAAO;;UAET,OAAO,UAAU,YAAY,UAAU,MAAM;EACvD,MAAM,QAAQ,SAAS,OAAO,SAAS;AACvC,MAAI,MAAO,QAAO;;AAIpB,QAAO;;AAGR,SAAgB,cAAc,KAAc,UAA0B;AACrE,KAAI,CAAC,OAAO,OAAO,QAAQ,SAAU,QAAO;CAE5C,IAAI,QAAQ;CACZ,MAAM,SAAS;AAEf,KAAI,YAAY,QAAQ;EACvB,MAAM,OAAO,OAAO;AACpB,WAAS,MAAM,QAAQ,KAAK,GAAG,KAAK,SAAS;;AAG9C,MAAK,MAAM,SAAS,OAAO,OAAO,OAAO,CACxC,KAAI,MAAM,QAAQ,MAAM,CACvB,MAAK,MAAM,QAAQ,MAClB,UAAS,cAAc,MAAM,SAAS;UAE7B,OAAO,UAAU,YAAY,UAAU,KACjD,UAAS,cAAc,OAAO,SAAS;AAIzC,QAAO;;;;;ACnDR,MAAa,qBAAqB;CACjC,UAAU;CACV,YAAY;CACZ,YAAY;CACZ,YAAY;CACZ,cAAc;CACd,cAAc;CACd,cAAc;CACd;AAED,MAAa,kBAAkB;CAC9B,MAAM;CACN,QAAQ;CACR,QAAQ;CACR,QAAQ;CACR;AAED,MAAa,yBAAyB;CACrC,SAAS;CACT,UAAU;CACV,iBAAiB;CACjB;AAED,MAAa,0BAA0B;CACtC,eAAe;CACf,aAAa;CACb,aAAa;CACb,aAAa;CACb,aAAa;CACb,aAAa;CACb,aAAa;CACb;AAED,MAAM,kCAAqD,CAC1D,mBAAmB,SACnB;AAED,MAAM,uCAA0D,CAC/D,uBAAuB,QACvB;AAED,MAAM,wCAA2D,CAChE,wBAAwB,cACxB;AAED,MAAM,+BAAkD,CAAC,gBAAgB,KAAK;AAE9E,MAAM,8BAAiD;CACtD,mBAAmB;CACnB,mBAAmB;CACnB,mBAAmB;CACnB,mBAAmB;CACnB,mBAAmB;CACnB,mBAAmB;CACnB;AAED,MAAM,2BAA8C;CACnD,gBAAgB;CAChB,gBAAgB;CAChB,gBAAgB;CAChB;AAED,MAAM,8BAAsD;CAC3D,MAAM,mBAAmB;CACzB,QAAQ,mBAAmB;CAC3B,QAAQ,mBAAmB;CAC3B,QAAQ,mBAAmB;CAC3B,YAAY,mBAAmB;CAC/B,cAAc,mBAAmB;CACjC,cAAc,mBAAmB;CACjC,cAAc,mBAAmB;CACjC,gBAAgB,mBAAmB;CACnC,gBAAgB,mBAAmB;CACnC,gBAAgB,mBAAmB;CACnC;AAED,MAAM,2BAAmD;CACxD,MAAM,gBAAgB;CACtB,QAAQ,gBAAgB;CACxB,QAAQ,gBAAgB;CACxB,QAAQ,gBAAgB;CACxB;AAED,SAAS,4BAA4B,KAAqB;AACzD,QAAO,4BAA4B,IAAI,aAAa,KAAK;;AAG1D,SAAS,yBAAyB,KAAqB;AACtD,QAAO,yBAAyB,IAAI,aAAa,KAAK;;AAavD,SAAS,4BAA4B,KAGnC;AACD,KAAI;EACH,MAAM,SAAS,UAAU,MAAM,IAAI;EAUnC,MAAM,UARe,SAAS,QAAQ,eAAe,EAIlB,oBAIL;EAU9B,MAAM,WARgB,SAAS,QAAQ,gBAAgB,EAIlB,oBAIL;AAEhC,SAAO;GACN,eAAe,UAAU;GACzB,gBAAgB,WAAW;GAC3B;SACM;AACP,SAAO;GACN,eAAe;GACf,gBAAgB;GAChB;;;AAIH,SAAS,sBAAsB,KAAsB;AACpD,KAAI;AAEH,SAAO,SADQ,UAAU,MAAM,IAAI,EACX,qBAAqB,KAAK;SAC3C;AACP,SAAO;;;AAIT,SAAS,0BACR,SACA,UACA,WACO;AACP,SAAQ,UAAR;EACC,KAAK,SACJ,OAAM,IAAI,SAAS,eAAe;GACjC;GACA,MAAM;GACN,CAAC;EACH,KAAK;AACJ,WAAQ,KAAK,2BAA2B,UAAU;AAClD;EACD,KAAK,QACJ;;;AAIH,SAAS,2BACR,WACA,UAAsC,EAAE,EACjC;AACP,KAAI,CAAC,UACJ;CAGD,MAAM,EAAE,eAAe,QAAQ,+BAA+B;AAE9D,KAAI,4BAA4B;AAC/B,MAAI,CAAC,2BAA2B,SAAS,UAAU,CAClD,OAAM,IAAI,SAAS,eAAe;GACjC,SAAS,+CAA+C;GACxD,MAAM;GACN,CAAC;AAEH;;AAGD,KAAI,gCAAgC,SAAS,UAAU,EAAE;AACxD,4BACC,sDAAsD,UAAU,0DAChE,cACA,4BACA;AACD;;AAGD,KAAI,CAAC,4BAA4B,SAAS,UAAU,CACnD,OAAM,IAAI,SAAS,eAAe;EACjC,SAAS,4CAA4C;EACrD,MAAM;EACN,CAAC;;AAIJ,SAAS,6BACR,YACA,UAAsC,EAAE,EACjC;CACP,MAAM,EACL,eAAe,QACf,gCACA,oCACG;CAEJ,MAAM,EAAE,eAAe,mBAAmB;AAE1C,KAAI,eACH;MAAI,gCACH;OAAI,CAAC,+BAA+B,SAAS,cAAc,CAC1D,OAAM,IAAI,SAAS,eAAe;IACjC,SAAS,oDAAoD;IAC7D,MAAM;IACN,CAAC;aAEO,qCAAqC,SAAS,cAAc,CACtE,2BACC,2DAA2D,cAAc,+CACzE,cACA,4BACA;;AAIH,KAAI,gBACH;MAAI,iCACH;OAAI,CAAC,gCAAgC,SAAS,eAAe,CAC5D,OAAM,IAAI,SAAS,eAAe;IACjC,SAAS,qDAAqD;IAC9D,MAAM;IACN,CAAC;aAEO,sCAAsC,SAAS,eAAe,CACxE,2BACC,4DAA4D,eAAe,8CAC3E,cACA,4BACA;;;AAKJ,SAAgB,uBACf,UACA,SACO;AACP,4BAA2B,SAAS,QAAQ,QAAQ;AAEpD,KAAI,sBAAsB,SAAS,YAAY,CAE9C,8BADgB,4BAA4B,SAAS,YAAY,EAC3B,QAAQ;;AAUhD,SAAgB,yBACf,QAIA,UAA4C,EAAE,EACvC;CACP,MAAM,EACL,eAAe,QACf,4BACA,4BACG;AAEJ,KAAI,OAAO,oBAAoB;EAC9B,MAAM,aAAa,4BAA4B,OAAO,mBAAmB;AACzE,MAAI,4BAIH;OAAI,CAHwB,2BAA2B,IACtD,4BACA,CACwB,SAAS,WAAW,CAC5C,OAAM,IAAI,SAAS,eAAe;IACjC,SAAS,+CAA+C,OAAO;IAC/D,MAAM;IACN,CAAC;aAEO,gCAAgC,SAAS,WAAW,CAC9D,2BACC,oDAAoD,OAAO,mBAAmB,wCAC9E,cACA,mCACA;WACS,CAAC,4BAA4B,SAAS,WAAW,CAC3D,OAAM,IAAI,SAAS,eAAe;GACjC,SAAS,4CAA4C,OAAO;GAC5D,MAAM;GACN,CAAC;;AAIJ,KAAI,OAAO,iBAAiB;EAC3B,MAAM,aAAa,yBAAyB,OAAO,gBAAgB;AACnE,MAAI,yBAIH;OAAI,CAHwB,wBAAwB,IACnD,yBACA,CACwB,SAAS,WAAW,CAC5C,OAAM,IAAI,SAAS,eAAe;IACjC,SAAS,4CAA4C,OAAO;IAC5D,MAAM;IACN,CAAC;aAEO,6BAA6B,SAAS,WAAW,CAC3D,2BACC,iDAAiD,OAAO,gBAAgB,wCACxE,cACA,mCACA;WACS,CAAC,yBAAyB,SAAS,WAAW,CACxD,OAAM,IAAI,SAAS,eAAe;GACjC,SAAS,yCAAyC,OAAO;GACzD,MAAM;GACN,CAAC;;;;;;;ACnUL,SAAgB,gBAAgB,KAA8B;CAC7D,IAAI;AACJ,KAAI;AACH,WAAS,UAAU,MAAM,IAAI;SACtB;AACP,QAAM,IAAI,SAAS,eAAe;GACjC,SAAS;GACT,MAAM;GACN,CAAC;;CAGH,MAAM,aAAa,cAAc,QAAQ,YAAY;CACrD,MAAM,sBAAsB,cAAc,QAAQ,qBAAqB;AAEvE,QAAO;EACN;EACA;EACA,OAAO,aAAa;EACpB;;AAGF,SAAgB,wBAAwB,cAA4B;CACnE,IAAI;AACJ,KAAI;AACH,QAAM,IAAI,aAAa,CAAC,OAAO,OAAO,OAAO,aAAa,CAAC;AAC3D,MAAI,CAAC,IAAI,SAAS,IAAI,CACrB,OAAM,IAAI,MAAM,UAAU;SAEpB;AACP,QAAM,IAAI,SAAS,eAAe;GACjC,SAAS;GACT,MAAM;GACN,CAAC;;CAGH,MAAM,SAAS,gBAAgB,IAAI;AAEnC,KAAI,OAAO,UAAU,EACpB,OAAM,IAAI,SAAS,eAAe;EACjC,SAAS;EACT,MAAM;EACN,CAAC;AAGH,KAAI,OAAO,QAAQ,EAClB,OAAM,IAAI,SAAS,eAAe;EACjC,SAAS,0BAA0B,OAAO,MAAM;EAChD,MAAM;EACN,CAAC;;;;;ACzDJ,MAAM,oBAAoB,EACxB,OAAO;CACP,IAAI,EAAE,QAAQ,CAAC,UAAU;CACzB,OAAO,EAAE,QAAQ,CAAC,UAAU;CAC5B,eAAe,EAAE,QAAQ,CAAC,UAAU;CACpC,MAAM,EAAE,QAAQ,CAAC,UAAU;CAC3B,OAAO,EAAE,QAAQ,CAAC,UAAU;CAC5B,aAAa,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE,KAAK,CAAC,CAAC,UAAU;CACrD,CAAC,CACD,UAAU;AAEZ,MAAM,oBAAoB,EACxB,OAAO;CACP,IAAI,EAAE,QAAQ,CAAC,UAAU;CACzB,OAAO,EAAE,QAAQ,CAAC,UAAU;CAC5B,eAAe,EAAE,QAAQ,CAAC,UAAU;CACpC,MAAM,EAAE,QAAQ,CAAC,UAAU;CAC3B,WAAW,EAAE,QAAQ,CAAC,UAAU;CAChC,UAAU,EAAE,QAAQ,CAAC,UAAU;CAC/B,aAAa,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE,KAAK,CAAC,CAAC,UAAU;CACrD,CAAC,CACD,UAAU;AAEZ,MAAM,mBAAmB,EAAE,OAAO;CACjC,UAAU,EAAE,QAAQ,CAAC,UAAU;CAC/B,cAAc,EAAE,QAAQ,CAAC,UAAU;CACnC,uBAAuB,EAAE,QAAQ,CAAC,KAAK,CAAC,UAAU;CAClD,eAAe,EAAE,QAAQ,CAAC,KAAK,CAAC,UAAU;CAC1C,kBAAkB,EAAE,QAAQ,CAAC,KAAK,CAAC,UAAU;CAC7C,6BAA6B,EAC3B,KAAK,CAAC,sBAAsB,sBAAsB,CAAC,CACnD,UAAU;CACZ,cAAc,EAAE,QAAQ,CAAC,KAAK,CAAC,UAAU;CACzC,mBAAmB,EAAE,QAAQ,CAAC,KAAK,CAAC,UAAU;CAC9C,QAAQ,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC,UAAU;CACtC,MAAM,EAAE,SAAS,CAAC,UAAU;CAC5B,kBAAkB,EAAE,SAAS,CAAC,UAAU;CACxC,SAAS;CACT,CAAC;AAEF,MAAM,mBAAmB,EAAE,OAAO;CACjC,YAAY,EAAE,QAAQ,CAAC,KAAK,CAAC,UAAU;CACvC,MAAM,EAAE,QAAQ,CAAC,UAAU;CAC3B,aAAa,EAAE,QAAQ,CAAC,KAAK,CAAC,UAAU;CACxC,UAAU,EAAE,QAAQ,CAAC,UAAU;CAC/B,aAAa,EACX,OAAO;EACP,UAAU,EAAE,QAAQ,CAAC,UAAU;EAC/B,UAAU,EAAE,QAAQ,CAAC,UAAU;EAC/B,MAAM,EAAE,QAAQ,CAAC,UAAU;EAC3B,YAAY,EAAE,QAAQ,CAAC,UAAU;EACjC,gBAAgB,EAAE,QAAQ,CAAC,UAAU;EACrC,sBAAsB,EAAE,SAAS,CAAC,UAAU;EAC5C,eAAe,EAAE,QAAQ,CAAC,UAAU;EACpC,mBAAmB,EAAE,QAAQ,CAAC,UAAU;EACxC,qBAAqB,EACnB,MACA,EAAE,OAAO;GACR,SAAS,EAAE,QAAQ;GACnB,UAAU,EAAE,QAAQ,CAAC,KAAK;GAC1B,CAAC,CACF,CACA,UAAU;EACZ,CAAC,CACD,UAAU;CACZ,YAAY,EACV,OAAO;EACP,UAAU,EAAE,QAAQ,CAAC,UAAU;EAC/B,UAAU,EAAE,QAAQ,CAAC,UAAU;EAC/B,SAAS,EAAE,QAAQ,CAAC,UAAU;EAC9B,YAAY,EAAE,QAAQ,CAAC,UAAU;EACjC,gBAAgB,EAAE,QAAQ,CAAC,UAAU;EACrC,sBAAsB,EAAE,SAAS,CAAC,UAAU;EAC5C,eAAe,EAAE,QAAQ,CAAC,UAAU;EACpC,mBAAmB,EAAE,QAAQ,CAAC,UAAU;EACxC,CAAC,CACD,UAAU;CACZ,sBAAsB,EAAE,SAAS,CAAC,UAAU;CAC5C,qBAAqB,EAAE,SAAS,CAAC,UAAU;CAC3C,oBAAoB,EAAE,QAAQ,CAAC,UAAU;CACzC,iBAAiB,EAAE,QAAQ,CAAC,UAAU;CACtC,kBAAkB,EAAE,QAAQ,CAAC,UAAU;CACvC,YAAY,EAAE,QAAQ,CAAC,UAAU;CACjC,eAAe,EAAE,QAAQ,CAAC,UAAU;CACpC,kBAAkB,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE,KAAK,CAAC,CAAC,UAAU;CAC1D,SAAS;CACT,CAAC;AAEF,MAAa,8BAA8B,EAAE,OAAO;CACnD,QAAQ,EAAE,QAAQ,CAAC,KAAK,CAAC,UAAU;CACnC,QAAQ,EAAE,QAAQ,CAAC,UAAU;CAC7B,YAAY,iBAAiB,UAAU;CACvC,YAAY,iBAAiB,UAAU;CACvC,CAAC;;;;ACtEF,MAAM,cAAc,CAAC,SAAS,QAAQ;AAEtC,eAAe,WACd,KAUA,QACA,gBACmB;CACnB,MAAM,SAAS,MAAM,IAAI,QAAQ,QAAQ,QAAgB;EACxD,OAAO;EACP,OAAO,CACN;GAAE,OAAO;GAAU,OAAO;GAAQ,EAClC;GAAE,OAAO;GAAkB,OAAO;GAAgB,CAClD;EACD,CAAC;AACF,KAAI,CAAC,OAAQ,QAAO;AAEpB,QADc,OAAO,KAAK,MAAM,IAAI,CACvB,MAAM,MAAM,YAAY,SAAS,EAAE,MAAM,CAAC,CAAC;;AAGzD,eAAe,mBACd,KAGA,QACA,iBACuB;AACvB,KAAI,gBAAgB,WAAW,EAC9B,wBAAO,IAAI,KAAK;CAGjB,MAAM,UAAU,MAAM,IAAI,QAAQ,QAAQ,SAAiB;EAC1D,OAAO;EACP,OAAO,CACN;GAAE,OAAO;GAAU,OAAO;GAAQ,EAClC;GAAE,OAAO;GAAkB,OAAO;GAAiB,UAAU;GAAM,CACnE;EACD,CAAC;CAEF,MAAM,8BAAc,IAAI,KAAa;AACrC,MAAK,MAAM,UAAU,QAEpB,KADc,OAAO,KAAK,MAAM,IAAI,CAC1B,MAAM,MAAc,YAAY,SAAS,EAAE,MAAM,CAAC,CAAC,CAC5D,aAAY,IAAI,OAAO,eAAe;AAIxC,QAAO;;AAGR,SAAS,iBACR,UASA,SACC;CACD,IAAI,aAAgC;CACpC,IAAI,aAAgC;AAEpC,KAAI;AACH,eAAa,cAA0B,SAAS,WAAqB;SAC9D;AACP,eAAa;;AAGd,KAAI;AACH,eAAa,cAA0B,SAAS,WAAqB;SAC9D;AACP,eAAa;;CAGd,MAAM,OAAO,aAAa,SAAS;AAEnC,QAAO;EACN,YAAY,SAAS;EACrB;EACA,QAAQ,SAAS;EACjB,QAAQ,SAAS;EACjB,gBAAgB,SAAS,kBAAkB;EAC3C,gBAAgB,SAAS,kBAAkB;EAC3C,YAAY,aACT;GACA,mBAAmB,WAAW;GAC9B,kBAAkB,aAAa,WAAW,SAAS;GACnD,MAAM,WAAW;GACjB,uBAAuB,WAAW;GAClC,eAAe,WAAW;GAC1B,kBAAkB,WAAW;GAC7B,cAAc,WAAW;GACzB,QAAQ,WAAW;GACnB,6BAA6B,WAAW;GACxC,GACA;EACH,YAAY,aACT;GACA,YAAY,WAAW;GACvB,aAAa,WAAW;GACxB,UAAU,WAAW;GACrB,sBAAsB,WAAW;GACjC,qBAAqB,WAAW;GAChC,kBAAkB,WAAW;GAC7B,oBAAoB,WAAW;GAC/B,iBAAiB,WAAW;GAC5B,oBAAoB;AACnB,QAAI;AACH,YAAO,iBAAiB,WAAW,KAAK;YACjC;AACP,YAAO,EAAE,OAAO,+BAA+B;;OAE7C;GACJ,GACA;EACH,eAAe,GAAG,QAAQ,oCAAoC,mBAAmB,SAAS,WAAW;EACrG;;AAGF,MAAa,yBAAyB;AACrC,QAAO,mBACN,kBACA;EACC,QAAQ;EACR,KAAK,CAAC,kBAAkB;EACxB,UAAU,EACT,SAAS;GACR,aAAa;GACb,SAAS;GACT,aAAa;GACb,WAAW,EACV,OAAO,EACN,aAAa,yBACb,EACD;GACD,EACD;EACD,EACD,OAAO,QAAQ;EACd,MAAM,SAAS,IAAI,QAAQ,QAAQ,KAAK;EAExC,MAAM,eACL,MAAM,IAAI,QAAQ,QAAQ,SAA4B,EACrD,OAAO,eACP,CAAC;EAEH,MAAM,qBAAqB,aAAa,QACtC,MAAM,EAAE,WAAW,UAAU,CAAC,EAAE,eACjC;EAED,MAAM,eAAe,aAAa,QAChC,MAAM,EAAE,mBAAmB,QAAQ,EAAE,mBAAmB,OACzD;EAED,MAAM,mBAAmB,IAAI,QAAQ,UAAU,eAAe;EAE9D,IAAI,sBAAiD,CACpD,GAAG,mBACH;AAED,MAAI,oBAAoB,aAAa,SAAS,GAAG;GAShD,MAAM,cAAc,MAAM,mBAAmB,KAAK,QARnC,CACd,GAAG,IAAI,IACN,aACE,KAAK,MAAM,EAAE,eAAe,CAC5B,QAAQ,OAAqB,OAAO,QAAQ,OAAO,OAAU,CAC/D,CACD,CAEgE;GAEjE,MAAM,yBAAyB,aAAa,QAC1C,aACA,SAAS,kBAAkB,YAAY,IAAI,SAAS,eAAe,CACpE;AAED,yBAAsB,CACrB,GAAG,qBACH,GAAG,uBACH;aACS,CAAC,kBAAkB;GAC7B,MAAM,wBAAwB,aAAa,QACzC,MAAM,EAAE,WAAW,OACpB;AACD,yBAAsB,CACrB,GAAG,qBACH,GAAG,sBACH;;EAGF,MAAM,YAAY,oBAAoB,KAAK,MAC1C,iBAAiB,GAAG,IAAI,QAAQ,QAAQ,CACxC;AAED,SAAO,IAAI,KAAK,EAAE,WAAW,CAAC;GAE/B;;AAGF,MAAM,6BAA6B,EAAE,OAAO,EAC3C,YAAY,EAAE,QAAQ,EACtB,CAAC;AAEF,eAAe,oBACd,KAKA,YACC;CACD,MAAM,SAAS,IAAI,QAAQ,QAAQ,KAAK;CAExC,MAAM,WAAW,MAAM,IAAI,QAAQ,QAAQ,QAA2B;EACrE,OAAO;EACP,OAAO,CAAC;GAAE,OAAO;GAAc,OAAO;GAAY,CAAC;EACnD,CAAC;AAEF,KAAI,CAAC,SACJ,OAAM,IAAI,SAAS,aAAa,EAC/B,SAAS,sBACT,CAAC;CAGH,IAAI,YAAY;AAChB,KAAI,SAAS,eACZ,KAAI,IAAI,QAAQ,UAAU,eAAe,CACxC,aAAY,MAAM,WAAW,KAAK,QAAQ,SAAS,eAAe;KAElE,aAAY,SAAS,WAAW;KAGjC,aAAY,SAAS,WAAW;AAGjC,KAAI,CAAC,UACJ,OAAM,IAAI,SAAS,aAAa,EAC/B,SAAS,0CACT,CAAC;AAGH,QAAO;;AAGR,MAAa,uBAAuB;AACnC,QAAO,mBACN,8BACA;EACC,QAAQ;EACR,KAAK,CAAC,kBAAkB;EACxB,QAAQ;EACR,UAAU,EACT,SAAS;GACR,aAAa;GACb,SAAS;GACT,aAAa;GACb,WAAW;IACV,OAAO,EACN,aAAa,wBACb;IACD,OAAO,EACN,aAAa,sBACb;IACD,OAAO,EACN,aAAa,iBACb;IACD;GACD,EACD;EACD,EACD,OAAO,QAAQ;EACd,MAAM,EAAE,eAAe,IAAI;EAE3B,MAAM,WAAW,MAAM,oBAAoB,KAAK,WAAW;AAE3D,SAAO,IAAI,KAAK,iBAAiB,UAAU,IAAI,QAAQ,QAAQ,CAAC;GAEjE;;AAGF,SAAS,uBACR,cACA,YACI;CACJ,IAAI,SAAmB;AACvB,KAAI;AACH,WAAS,cAAiB,aAAuB;SAC1C;AACP,WAAS;;AAEV,KAAI,CAAC,OACJ,OAAM,IAAI,SAAS,eAAe,EACjC,SAAS,iBAAiB,WAAW,2CAA2C,WAAW,cAC3F,CAAC;AAEH,QAAO;;AAGR,SAAS,gBACR,SACA,SACA,QACa;AACb,QAAO;EACN,GAAG;EACH,GAAG;EACH;EACA,YAAY,QAAQ,cAAc,QAAQ;EAC1C,MAAM,QAAQ,QAAQ,QAAQ;EAC9B,aAAa,QAAQ,eAAe,QAAQ;EAC5C,YAAY,QAAQ,cAAc,QAAQ;EAC1C,aAAa,QAAQ,eAAe,QAAQ;EAC5C,SAAS,QAAQ,WAAW,QAAQ;EACpC,UAAU,QAAQ,YAAY,QAAQ;EACtC,sBACC,QAAQ,wBAAwB,QAAQ;EACzC,qBACC,QAAQ,uBAAuB,QAAQ;EACxC,kBAAkB,QAAQ,oBAAoB,QAAQ;EACtD,oBACC,QAAQ,sBAAsB,QAAQ;EACvC,iBAAiB,QAAQ,mBAAmB,QAAQ;EACpD;;AAGF,SAAS,gBACR,SACA,SACA,QACa;AACb,QAAO;EACN,GAAG;EACH,GAAG;EACH;EACA,MAAM,QAAQ,QAAQ,QAAQ,QAAQ;EACtC,UAAU,QAAQ,YAAY,QAAQ;EACtC,cAAc,QAAQ,gBAAgB,QAAQ;EAC9C,mBAAmB,QAAQ,qBAAqB,QAAQ;EACxD,SAAS,QAAQ,WAAW,QAAQ;EACpC,QAAQ,QAAQ,UAAU,QAAQ;EAClC,uBACC,QAAQ,yBAAyB,QAAQ;EAC1C,eAAe,QAAQ,iBAAiB,QAAQ;EAChD,kBAAkB,QAAQ,oBAAoB,QAAQ;EACtD,cAAc,QAAQ,gBAAgB,QAAQ;EAC9C,6BACC,QAAQ,+BACR,QAAQ;EACT;;AAGF,MAAa,qBAAqB,YAAwB;AACzD,QAAO,mBACN,8BACA;EACC,QAAQ;EACR,KAAK,CAAC,kBAAkB;EACxB,QAAQ;EACR,MAAM;EACN,UAAU,EACT,SAAS;GACR,aAAa;GACb,SAAS;GACT,aACC;GACD,WAAW;IACV,OAAO,EACN,aAAa,qCACb;IACD,OAAO,EACN,aAAa,sBACb;IACD,OAAO,EACN,aAAa,iBACb;IACD;GACD,EACD;EACD,EACD,OAAO,QAAQ;EACd,MAAM,EAAE,eAAe,IAAI;EAC3B,MAAM,OAAO,IAAI;EAEjB,MAAM,EAAE,QAAQ,QAAQ,YAAY,eAAe;AACnD,MAAI,CAAC,UAAU,CAAC,UAAU,CAAC,cAAc,CAAC,WACzC,OAAM,IAAI,SAAS,eAAe,EACjC,SAAS,iCACT,CAAC;EAGH,MAAM,mBAAmB,MAAM,oBAAoB,KAAK,WAAW;EAEnE,MAAM,aAAyC,EAAE;AAEjD,MAAI,KAAK,WAAW,OACnB,YAAW,SAAS,KAAK;AAG1B,MAAI,KAAK,WAAW,QAAW;AAC9B,cAAW,SAAS,KAAK;AACzB,OAAI,KAAK,WAAW,iBAAiB,OACpC,YAAW,iBAAiB;;AAI9B,MAAI,KAAK,YAAY;AACpB,OAAI,KAAK,WAAW,aAAa,UAAU;IAC1C,MAAM,kBACL,SAAS,MAAM,mBAAmB;AACnC,QACC,IAAI,aAAa,CAAC,OAAO,KAAK,WAAW,YAAY,SAAS,CAC5D,SAAS,gBAEX,OAAM,IAAI,SAAS,eAAe,EACjC,SAAS,8CAA8C,gBAAgB,UACvE,CAAC;;AAIJ,OACC,KAAK,WAAW,uBAAuB,UACvC,KAAK,WAAW,oBAAoB,OAEpC,0BACC;IACC,oBAAoB,KAAK,WAAW;IACpC,iBAAiB,KAAK,WAAW;IACjC,EACD,SAAS,MAAM,WACf;GAGF,MAAM,oBAAoB,uBACzB,iBAAiB,YACjB,OACA;GAED,MAAM,oBAAoB,gBACzB,mBACA,KAAK,YACL,WAAW,UACV,kBAAkB,UAClB,iBAAiB,OAClB;AAED,cAAW,aAAa,KAAK,UAAU,kBAAkB;;AAG1D,MAAI,KAAK,YAAY;GACpB,MAAM,oBAAoB,uBACzB,iBAAiB,YACjB,OACA;GAED,MAAM,oBAAoB,gBACzB,mBACA,KAAK,YACL,WAAW,UACV,kBAAkB,UAClB,iBAAiB,OAClB;AAED,cAAW,aAAa,KAAK,UAAU,kBAAkB;;AAG1D,QAAM,IAAI,QAAQ,QAAQ,OAAO;GAChC,OAAO;GACP,OAAO,CAAC;IAAE,OAAO;IAAc,OAAO;IAAY,CAAC;GACnD,QAAQ;GACR,CAAC;EAEF,MAAM,eAAe,MAAM,IAAI,QAAQ,QAAQ,QAC9C;GACC,OAAO;GACP,OAAO,CAAC;IAAE,OAAO;IAAc,OAAO;IAAY,CAAC;GACnD,CACD;AAED,MAAI,CAAC,aACJ,OAAM,IAAI,SAAS,aAAa,EAC/B,SAAS,mCACT,CAAC;AAGH,SAAO,IAAI,KAAK,iBAAiB,cAAc,IAAI,QAAQ,QAAQ,CAAC;GAErE;;AAGF,MAAa,0BAA0B;AACtC,QAAO,mBACN,8BACA;EACC,QAAQ;EACR,KAAK,CAAC,kBAAkB;EACxB,QAAQ;EACR,UAAU,EACT,SAAS;GACR,aAAa;GACb,SAAS;GACT,aAAa;GACb,WAAW;IACV,OAAO,EACN,aAAa,qCACb;IACD,OAAO,EACN,aAAa,sBACb;IACD,OAAO,EACN,aAAa,iBACb;IACD;GACD,EACD;EACD,EACD,OAAO,QAAQ;EACd,MAAM,EAAE,eAAe,IAAI;AAE3B,QAAM,oBAAoB,KAAK,WAAW;AAE1C,QAAM,IAAI,QAAQ,QAAQ,OAAO;GAChC,OAAO;GACP,OAAO,CAAC;IAAE,OAAO;IAAc,OAAO;IAAY,CAAC;GACnD,CAAC;AAEF,SAAO,IAAI,KAAK,EAAE,SAAS,MAAM,CAAC;GAEnC;;;;;;;;;AC3bF,IAAa,iBAAb,MAAa,uBAAuB,MAAM;CACzC,AAAgB;CAChB,AAAgB;CAEhB,YACC,MACA,SACA,SACA,SACC;AACD,QAAM,SAAS,QAAQ;AACvB,OAAK,OAAO;AACZ,OAAK,OAAO;AACZ,OAAK,UAAU;AAGf,MAAI,MAAM,kBACT,OAAM,kBAAkB,MAAM,eAAe;;;;;;AA0EhD,MAAa,4BAA4B;CACxC;CACA;CACA;CACA;CACA;;;;;;;;;;;;;;ACrMD,MAAM,4BAA4B;;;;;;;;;;;;;;;;;;AAmBlC,eAAsB,mBACrB,QAC8B;CAC9B,MAAM,EACL,QACA,gBACA,UAAU,8BACP;CAEJ,MAAM,eACL,OAAO,qBACP,gBAAgB,qBAChB,oBAAoB,OAAO;AAE5B,sBAAqB,cAAc,OAAO,gBAAgB;CAE1D,MAAM,eAAe,MAAM,uBAAuB,cAAc,QAAQ;AAExE,2BAA0B,cAAc,OAAO;CAE/C,MAAM,gBAAgB,uBACrB,cACA,QACA,OAAO,gBACP;CAED,MAAM,oBAAoB,8BACzB,eACA,gBAAgB,4BAChB;AAmBD,QAjB2C;EAC1C,QAAQ,gBAAgB,UAAU,cAAc;EAChD,mBAAmB,gBAAgB,qBAAqB;EACxD,uBACC,gBAAgB,yBAChB,cAAc;EACf,eACC,gBAAgB,iBAAiB,cAAc;EAChD,cAAc,gBAAgB,gBAAgB,cAAc;EAC5D,kBACC,gBAAgB,oBAAoB,cAAc;EACnD,6BACC,gBAAgB,+BAA+B;EAChD,iBACC,gBAAgB,mBAAmB,cAAc;EAClD;;;;;;;;;;AAaF,SAAgB,oBAAoB,QAAwB;AAE3D,QAAO,GADS,OAAO,SAAS,IAAI,GAAG,OAAO,MAAM,GAAG,GAAG,GAAG,OAC3C;;;;;;;;;AAUnB,SAAgB,qBACf,KACA,iBACO;CACP,MAAM,oBAAoB,SAAS,qBAAqB,IAAI,CAAC,UAAU;AAEvE,KAAI,CAAC,gBAAgB,kBAAkB,CACtC,OAAM,IAAI,eACT,8BACA,gCAAgC,kBAAkB,0DAClD,EAAE,KAAK,mBAAmB,CAC1B;;;;;;;;;;AAYH,eAAsB,uBACrB,KACA,UAAkB,2BACe;AACjC,KAAI;EACH,MAAM,WAAW,MAAM,YAAmC,KAAK;GAC9D,QAAQ;GACR;GACA,CAAC;AAEF,MAAI,SAAS,OAAO;GACnB,MAAM,EAAE,WAAW,SAAS;AAE5B,OAAI,WAAW,IACd,OAAM,IAAI,eACT,uBACA,gCACA;IACC;IACA;IACA,CACD;AAGF,OAAI,WAAW,IACd,OAAM,IAAI,eACT,qBACA,+BACA;IACC;IACA;IACA,CACD;AAGF,SAAM,IAAI,eACT,8BACA,+BAA+B,SAAS,MAAM,cAC9C;IAAE;IAAK,GAAG,SAAS;IAAO,CAC1B;;AAGF,MAAI,CAAC,SAAS,KACb,OAAM,IAAI,eACT,0BACA,iDACA,EAAE,KAAK,CACP;EAGF,MAAM,OAAO,SAAS;AACtB,MAAI,OAAO,SAAS,SACnB,OAAM,IAAI,eACT,0BACA,4CACA;GAAE;GAAK,aAAa,KAAK,MAAM,GAAG,IAAI;GAAE,CACxC;AAGF,SAAO;UACC,OAAO;AACf,MAAI,iBAAiB,eACpB,OAAM;AAKP,MAAI,iBAAiB,SAAS,MAAM,SAAS,aAC5C,OAAM,IAAI,eACT,qBACA,+BACA;GACC;GACA;GACA,CACD;AAGF,QAAM,IAAI,eACT,8BACA,sCAAsC,iBAAiB,QAAQ,MAAM,UAAU,OAAO,MAAM,IAC5F,EAAE,KAAK,EACP,EAAE,OAAO,OAAO,CAChB;;;;;;;;;;;;;;;;;;AAmBH,SAAgB,0BACf,KACA,kBACO;CACP,MAAM,gBAA0B,EAAE;AAElC,MAAK,MAAM,SAAS,0BACnB,KAAI,CAAC,IAAI,OACR,eAAc,KAAK,MAAM;AAI3B,KAAI,cAAc,SAAS,EAC1B,OAAM,IAAI,eACT,wBACA,kDAAkD,cAAc,KAAK,KAAK,IAC1E,EAAE,eAAe,CACjB;AAUF,MAPyB,IAAI,OAAO,SAAS,IAAI,GAC9C,IAAI,OAAO,MAAM,GAAG,GAAG,GACvB,IAAI,aACgB,iBAAiB,SAAS,IAAI,GAClD,iBAAiB,MAAM,GAAG,GAAG,GAC7B,kBAGF,OAAM,IAAI,eACT,mBACA,sBAAsB,IAAI,OAAO,sCAAsC,iBAAiB,IACxF;EACC,YAAY,IAAI;EAChB,YAAY;EACZ,CACD;;;;;;;;;;AAYH,SAAgB,uBACf,UACA,QACA,iBACwB;CACxB,MAAM,MAAM,EAAE,GAAG,UAAU;AAE3B,KAAI,iBAAiB,wBACpB,kBACA,IAAI,gBACJ,QACA,gBACA;AACD,KAAI,yBAAyB,wBAC5B,0BACA,IAAI,wBACJ,QACA,gBACA;AAED,KAAI,WAAW,wBACd,YACA,IAAI,UACJ,QACA,gBACA;AAED,KAAI,IAAI,kBACP,KAAI,oBAAoB,wBACvB,qBACA,IAAI,mBACJ,QACA,gBACA;AAGF,KAAI,IAAI,oBACP,KAAI,sBAAsB,wBACzB,uBACA,IAAI,qBACJ,QACA,gBACA;AAGF,KAAI,IAAI,qBACP,KAAI,uBAAuB,wBAC1B,wBACA,IAAI,sBACJ,QACA,gBACA;AAGF,KAAI,IAAI,uBACP,KAAI,yBAAyB,wBAC5B,0BACA,IAAI,wBACJ,QACA,gBACA;AAGF,QAAO;;;;;;;;;;AAWR,SAAS,wBACR,MACA,UACA,QACA,iBACS;CACT,MAAM,MAAM,aAAa,MAAM,UAAU,OAAO;AAEhD,KAAI,CAAC,gBAAgB,IAAI,CACxB,OAAM,IAAI,eACT,8BACA,OAAO,KAAK,IAAI,IAAI,0DACpB;EAAE,UAAU;EAAM;EAAK,CACvB;AAGF,QAAO;;;;;;;;;;AAWR,SAAgB,aACf,MACA,UACA,QACS;AACT,KAAI;AACH,SAAO,SAAS,MAAM,SAAS,CAAC,UAAU;SACnC;EAIP,MAAM,YAAY,SAAS,MAAM,OAAO;EACxC,MAAM,WAAW,UAAU,SAAS,QAAQ,QAAQ,GAAG;EACvD,MAAM,eAAe,SAAS,QAAQ,QAAQ,GAAG;AAEjD,SAAO,SACN,MACA,WAAW,MAAM,cACjB,UAAU,OACV,CAAC,UAAU;;;;;;;;;;;AAYd,SAAS,SAAS,MAAc,UAAkB,MAAe;CAChE,IAAI;AAEJ,KAAI;AACH,gBAAc,IAAI,IAAI,UAAU,KAAK;AACrC,MAAI,YAAY,aAAa,WAAW,YAAY,aAAa,SAChE,QAAO;UAEA,OAAO;AACf,QAAM,IAAI,eACT,yBACA,YAAY,KAAK,mBAAmB,YACpC,EACC,KAAK,UACL,EACD,EAAE,OAAO,OAAO,CAChB;;AAGF,OAAM,IAAI,eACT,yBACA,YAAY,KAAK,oDAAoD,YACrE;EAAE,KAAK;EAAU,UAAU,YAAY;EAAU,CACjD;;;;;;;;;AAUF,SAAgB,8BACf,KACA,UAC+C;AAC/C,KAAI,SACH,QAAO;CAGR,MAAM,YAAY,IAAI;AAEtB,KAAI,CAAC,aAAa,UAAU,WAAW,EACtC,QAAO;AAGR,KAAI,UAAU,SAAS,sBAAsB,CAC5C,QAAO;AAGR,KAAI,UAAU,SAAS,qBAAqB,CAC3C,QAAO;AAGR,QAAO;;;;;;;;;;;;;;;;AAiBR,SAAgB,sBACf,QACU;AACV,KAAI,CAAC,OACJ,QAAO;AAGR,QAAO,CAAC,OAAO,iBAAiB,CAAC,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;ACldzC,SAAgB,4BAA4B,OAAiC;AAC5E,SAAQ,MAAM,MAAd;EACC,KAAK,oBACJ,QAAO,IAAI,SAAS,eAAe;GAClC,SAAS,6BAA6B,MAAM;GAC5C,MAAM,MAAM;GACZ,CAAC;EAEH,KAAK,6BACJ,QAAO,IAAI,SAAS,eAAe;GAClC,SAAS,0BAA0B,MAAM;GACzC,MAAM,MAAM;GACZ,CAAC;EAEH,KAAK,sBACJ,QAAO,IAAI,SAAS,eAAe;GAClC,SAAS,0GAA0G,MAAM;GACzH,MAAM,MAAM;GACZ,CAAC;EAEH,KAAK,wBACJ,QAAO,IAAI,SAAS,eAAe;GAClC,SAAS,+BAA+B,MAAM;GAC9C,MAAM,MAAM;GACZ,CAAC;EAEH,KAAK,6BACJ,QAAO,IAAI,SAAS,eAAe;GAClC,SAAS,iCAAiC,MAAM;GAChD,MAAM,MAAM;GACZ,CAAC;EAEH,KAAK,yBACJ,QAAO,IAAI,SAAS,eAAe;GAClC,SAAS,yCAAyC,MAAM;GACxD,MAAM,MAAM;GACZ,CAAC;EAEH,KAAK,uBACJ,QAAO,IAAI,SAAS,eAAe;GAClC,SAAS,uDAAuD,MAAM;GACtE,MAAM,MAAM;GACZ,CAAC;EAEH,KAAK,kBACJ,QAAO,IAAI,SAAS,eAAe;GAClC,SAAS,yBAAyB,MAAM;GACxC,MAAM,MAAM;GACZ,CAAC;EAEH,KAAK,gCACJ,QAAO,IAAI,SAAS,eAAe;GAClC,SAAS,+BAA+B,MAAM;GAC9C,MAAM,MAAM;GACZ,CAAC;EAEH;AAEiC,SAAM;AACtC,UAAO,IAAI,SAAS,yBAAyB;IAC5C,SAAS,+BAA+B,MAAM;IAC9C,MAAM;IACN,CAAC;;;;;;ACnFL,eAAsB,mBACrB,GACA,MAMA,gBACC;CACD,MAAM,cAAc,EAAE,KAAK;AAC3B,KAAI,CAAC,YACJ,OAAM,IAAIC,WAAS,eAAe,EACjC,SAAS,2BACT,CAAC;CAGH,MAAM,eAAe,qBAAqB,IAAI;CAC9C,MAAM,YAAuB;EAC5B,GAAI,iBAAiB,iBAAiB,EAAE;EACxC;EACA;EACA,UAAU,EAAE,KAAK;EACjB,YAAY,EAAE,KAAK;EACnB;EAIA,WAAW,KAAK,KAAK,GAAG,MAAU;EAClC,eAAe,EAAE,KAAK;EACtB;AAED,KAAI;AACH,SAAO,qBAAqB,GAAG,WAAW,EACzC,YAAY,eACZ,CAAC;UACM,OAAO;AACf,IAAE,QAAQ,OAAO,MAChB,iDACA,MACA;AACD,QAAM,IAAIA,WAAS,yBAAyB;GAC3C,SAAS;GACT,OAAO;GACP,CAAC;;;AAIJ,eAAsB,gBAAgB,GAA2B;CAChE,MAAM,QAAQ,EAAE,KAAK;CACrB,MAAM,WACL,EAAE,QAAQ,QAAQ,YAAY,YAAY,GAAG,EAAE,QAAQ,QAAQ;CAEhE,IAAI;AAEJ,KAAI;AACH,eAAa,MAAM,kBAAkB,GAAG,OAAO,EAC9C,YAAY,eACZ,CAAC;UACM,OAAO;AACf,IAAE,QAAQ,OAAO,MAAM,+BAA+B,MAAM;AAC5D,QAAM,IAAIA,WAAS,eAAe;GACjC,SAAS;GACT,OAAO;GACP,CAAC;;AAGH,KAAI,CAAC,WAAW,SACf,YAAW,WAAW;AAGvB,QAAO;;;;;;;;;;ACER,SAAgB,sBACf,YACA,UAAsC,EAAE,EACjC;CACP,MAAM,YAAY,QAAQ,aAAa;AAGvC,KAAI,EAFkB,YAAY,aAAa,YAAY,eAEvC;AACnB,MAAI,QAAQ,kBACX,OAAM,IAAI,SAAS,eAAe;GACjC,SAAS;GACT,SACC;GACD,CAAC;AAGH,UAAQ,QAAQ,KACf,wDACA,EAAE,eAAe,CAAC,CAAC,YAAY,CAC/B;AACD;;CAGD,MAAM,MAAM,KAAK,KAAK;AAEtB,KAAI,YAAY,WAAW;EAC1B,MAAM,gBAAgB,IAAI,KAAK,WAAW,UAAU,CAAC,SAAS;AAC9D,MAAI,OAAO,MAAM,cAAc,CAC9B,OAAM,IAAI,SAAS,eAAe;GACjC,SAAS;GACT,SAAS,oCAAoC,WAAW;GACxD,CAAC;AAEH,MAAI,MAAM,gBAAgB,UACzB,OAAM,IAAI,SAAS,eAAe;GACjC,SAAS;GACT,SAAS,0CAA0C,UAAU;GAC7D,CAAC;;AAIJ,KAAI,YAAY,cAAc;EAC7B,MAAM,mBAAmB,IAAI,KAAK,WAAW,aAAa,CAAC,SAAS;AACpE,MAAI,OAAO,MAAM,iBAAiB,CACjC,OAAM,IAAI,SAAS,eAAe;GACjC,SAAS;GACT,SAAS,uCAAuC,WAAW;GAC3D,CAAC;AAEH,MAAI,MAAM,mBAAmB,UAC5B,OAAM,IAAI,SAAS,eAAe;GACjC,SAAS;GACT,SAAS,4CAA4C,UAAU;GAC/D,CAAC;;;;;;;AASL,SAAS,mBAAmB,aAAoC;AAC/D,KAAI;EAMH,MAAM,SALS,IAAI,UAAU;GAC5B,kBAAkB;GAClB,qBAAqB;GACrB,gBAAgB;GAChB,CAAC,CACoB,MAAM,YAAY;EAExC,MAAM,WAAW,OAAO,YAAY,OAAO;AAC3C,MAAI,CAAC,SAAU,QAAO;EAEtB,MAAM,eAAe,SAAS,aAAa,SAAS;EACpD,MAAM,YAAY,MAAM,QAAQ,aAAa,GAC1C,aAAa,KACb;AACH,MAAI,CAAC,UAAW,QAAO;AAEvB,SAAO,UAAU,WAAW;SACrB;AACP,SAAO;;;AAIT,MAAM,wBAAwB,EAAE,OAAO;CACtC,YAAY,EAAE,QAAQ;CACtB,QAAQ,EAAE,KAAK,CAAC,OAAO,OAAO,CAAC,CAAC,QAAQ,MAAM;CAC9C,CAAC;AAIF,MAAa,mBAAmB;AAC/B,QAAO,mBACN,0BACA;EACC,QAAQ;EACR,OAAO;EACP,UAAU,EACT,SAAS;GACR,aAAa;GACb,SAAS;GACT,aAAa;GACb,WAAW,EACV,OAAO,EACN,aAAa,+BACb,EACD;GACD,EACD;EACD,EACD,OAAO,QAAQ;EACd,MAAM,WAAW,MAAM,IAAI,QAAQ,QAAQ,QAGxC;GACF,OAAO;GACP,OAAO,CACN;IACC,OAAO;IACP,OAAO,IAAI,MAAM;IACjB,CACD;GACD,CAAC;AACF,MAAI,CAAC,SACJ,OAAM,IAAI,SAAS,aAAa,EAC/B,SAAS,8CACT,CAAC;EAGH,MAAM,mBAAmB,cAA0B,SAAS,WAAW;AACvE,MAAI,CAAC,iBACJ,OAAM,IAAI,SAAS,eAAe,EACjC,SAAS,8BACT,CAAC;EAEH,MAAM,KAAK,iBAAiB,WAAW,WACpC,KAAK,gBAAgB,EACrB,UAAU,iBAAiB,WAAW,UACtC,CAAC,GACD,KAAK,WAAW;GAChB,UACC,iBAAiB,YAAY,YAAY,iBAAiB;GAC3D,0BAA0B,CACzB;IACC,SAAS;IACT,UACC,iBAAiB,eACjB,GAAG,IAAI,QAAQ,QAAQ,oBAAoB,SAAS;IACrD,CACD;GACD,mBAAmB,iBAAiB,wBAAwB;GAC5D,qBAAqB,iBAAiB,uBAAuB;GAC7D,cAAc,iBAAiB,mBAC5B,CAAC,iBAAiB,iBAAiB,GACnC;GACH,CAAC;AACJ,SAAO,IAAI,SAAS,GAAG,aAAa,EAAE,EACrC,SAAS,EACR,gBAAgB,mBAChB,EACD,CAAC;GAEH;;AAGF,MAAM,wBAAwB,EAAE,OAAO;CACtC,YAAY,EAAE,OAAO,EAAE,CAAC,CAAC,KAAK,EAC7B,aACC,2FACD,CAAC;CACF,QAAQ,EAAE,OAAO,EAAE,CAAC,CAAC,KAAK,EACzB,aAAa,8BACb,CAAC;CACF,QAAQ,EAAE,OAAO,EAAE,CAAC,CAAC,KAAK,EACzB,aACC,yMACD,CAAC;CACF,YAAY,EACV,OAAO;EACP,UAAU,EAAE,OAAO,EAAE,CAAC,CAAC,KAAK,EAC3B,aAAa,iBACb,CAAC;EACF,cAAc,EAAE,OAAO,EAAE,CAAC,CAAC,KAAK,EAC/B,aAAa,qBACb,CAAC;EACF,uBAAuB,EACrB,OAAO,EAAE,CAAC,CACV,KAAK,EACL,aAAa,8BACb,CAAC,CACD,UAAU;EACZ,eAAe,EACb,OAAO,EAAE,CAAC,CACV,KAAK,EACL,aAAa,sBACb,CAAC,CACD,UAAU;EACZ,kBAAkB,EAChB,OAAO,EAAE,CAAC,CACV,KAAK,EACL,aAAa,0BACb,CAAC,CACD,UAAU;EACZ,6BAA6B,EAC3B,KAAK,CAAC,sBAAsB,sBAAsB,CAAC,CACnD,UAAU;EACZ,cAAc,EACZ,OAAO,EAAE,CAAC,CACV,KAAK,EACL,aAAa,qBACb,CAAC,CACD,UAAU;EACZ,mBAAmB,EAAE,QAAQ,CAAC,UAAU;EACxC,eAAe,EACb,SAAS,CACT,KAAK,EACL,aACC,yIACD,CAAC,CACD,UAAU;EACZ,QAAQ,EACN,MAAM,EAAE,QAAQ,EAAE,EAAE,CAAC,CACrB,KAAK,EACL,aACC,uFACD,CAAC,CACD,UAAU;EACZ,MAAM,EACJ,QAAQ,EAAE,CAAC,CACX,KAAK,EACL,aAAa,kDACb,CAAC,CACD,QAAQ,KAAK,CACb,UAAU;EACZ,SAAS,EACP,OAAO;GACP,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC,KAAK,EACrB,aAAa,iDACb,CAAC;GACF,OAAO,EAAE,OAAO,EAAE,CAAC,CAAC,KAAK,EACxB,aAAa,iDACb,CAAC;GACF,eAAe,EACb,OAAO,EAAE,CAAC,CACV,KAAK,EACL,aACC,uEACD,CAAC,CACD,UAAU;GACZ,MAAM,EAAE,OAAO,EAAE,CAAC,CAAC,KAAK,EACvB,aAAa,+CACb,CAAC;GACF,OAAO,EACL,OAAO,EAAE,CAAC,CACV,KAAK,EACL,aAAa,mDACb,CAAC,CACD,UAAU;GACZ,aAAa,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE,KAAK,CAAC,CAAC,UAAU;GACrD,CAAC,CACD,UAAU;EACZ,CAAC,CACD,UAAU;CACZ,YAAY,EACV,OAAO;EACP,YAAY,EAAE,OAAO,EAAE,CAAC,CAAC,KAAK,EAC7B,aAAa,mCACb,CAAC;EACF,MAAM,EAAE,OAAO,EAAE,CAAC,CAAC,KAAK,EACvB,aAAa,mCACb,CAAC;EACF,aAAa,EAAE,OAAO,EAAE,CAAC,CAAC,KAAK,EAC9B,aAAa,oCACb,CAAC;EACF,UAAU,EAAE,QAAQ,CAAC,UAAU;EAC/B,aAAa,EACX,OAAO;GACP,UAAU,EAAE,QAAQ,CAAC,UAAU;GAC/B,UAAU,EAAE,QAAQ,CAAC,UAAU;GAC/B,MAAM,EAAE,QAAQ,CAAC,UAAU;GAC3B,YAAY,EAAE,QAAQ,CAAC,UAAU;GACjC,gBAAgB,EAAE,QAAQ,CAAC,UAAU;GACrC,sBAAsB,EAAE,SAAS,CAAC,UAAU;GAC5C,eAAe,EAAE,QAAQ,CAAC,UAAU;GACpC,mBAAmB,EAAE,QAAQ,CAAC,UAAU;GACxC,qBAAqB,EACnB,MACA,EAAE,OAAO;IACR,SAAS,EAAE,QAAQ,CAAC,KAAK,EACxB,aAAa,wCACb,CAAC;IACF,UAAU,EAAE,QAAQ,CAAC,KAAK,EACzB,aAAa,+BACb,CAAC;IACF,CAAC,CACF,CACA,UAAU,CACV,KAAK,EACL,aAAa,wCACb,CAAC;GACH,CAAC,CACD,UAAU;EACZ,YAAY,EAAE,OAAO;GACpB,UAAU,EAAE,QAAQ,CAAC,UAAU;GAC/B,UAAU,EAAE,QAAQ,CAAC,UAAU;GAC/B,SAAS,EAAE,QAAQ,CAAC,UAAU;GAC9B,YAAY,EAAE,QAAQ,CAAC,UAAU;GACjC,gBAAgB,EAAE,QAAQ,CAAC,UAAU;GACrC,sBAAsB,EAAE,SAAS,CAAC,UAAU;GAC5C,eAAe,EAAE,QAAQ,CAAC,UAAU;GACpC,mBAAmB,EAAE,QAAQ,CAAC,UAAU;GACxC,CAAC;EACF,sBAAsB,EAAE,SAAS,CAAC,UAAU;EAC5C,qBAAqB,EAAE,SAAS,CAAC,UAAU;EAC3C,oBAAoB,EAAE,QAAQ,CAAC,UAAU;EACzC,iBAAiB,EAAE,QAAQ,CAAC,UAAU;EACtC,kBAAkB,EAAE,QAAQ,CAAC,UAAU;EACvC,YAAY,EAAE,QAAQ,CAAC,UAAU;EACjC,eAAe,EAAE,QAAQ,CAAC,UAAU;EACpC,kBAAkB,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE,KAAK,CAAC,CAAC,UAAU;EAC1D,SAAS,EACP,OAAO;GACP,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC,KAAK,EACrB,aAAa,oDACb,CAAC;GACF,OAAO,EAAE,OAAO,EAAE,CAAC,CAAC,KAAK,EACxB,aAAa,iDACb,CAAC;GACF,eAAe,EACb,OAAO,EAAE,CAAC,CACV,KAAK,EACL,aAAa,wCACb,CAAC,CACD,UAAU;GACZ,MAAM,EAAE,OAAO,EAAE,CAAC,CAAC,KAAK,EACvB,aAAa,sDACb,CAAC;GACF,WAAW,EACT,OAAO,EAAE,CAAC,CACV,KAAK,EACL,aACC,0DACD,CAAC,CACD,UAAU;GACZ,UAAU,EACR,OAAO,EAAE,CAAC,CACV,KAAK,EACL,aACC,uDACD,CAAC,CACD,UAAU;GACZ,aAAa,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE,KAAK,CAAC,CAAC,UAAU;GACrD,CAAC,CACD,UAAU;EACZ,CAAC,CACD,UAAU;CACZ,gBAAgB,EACd,OAAO,EAAE,CAAC,CACV,KAAK,EACL,aACC,kFACD,CAAC,CACD,UAAU;CACZ,kBAAkB,EAChB,QAAQ,EAAE,CAAC,CACX,KAAK,EACL,aACC,gEACD,CAAC,CACD,QAAQ,MAAM,CACd,UAAU;CACZ,CAAC;AAEF,MAAa,uBAA6C,YAAe;AACxE,QAAO,mBACN,iBACA;EACC,QAAQ;EACR,MAAM;EACN,KAAK,CAAC,kBAAkB;EACxB,UAAU,EACT,SAAS;GACR,aAAa;GACb,SAAS;GACT,aACC;GACD,WAAW,EACV,OAAO;IACN,aAAa;IACb,SAAS,EACR,oBAAoB,EACnB,QAAQ;KACP,MAAM;KACN,YAAY;MACX,QAAQ;OACP,MAAM;OACN,QAAQ;OACR,aAAa;OACb;MACD,QAAQ;OACP,MAAM;OACN,aACC;OACD;MACD,gBAAgB;OACf,MAAM;OACN,aACC;OACD;MACD,yBAAyB;OACxB,MAAM;OACN,aACC;OACD;MACD,YAAY;OACX,MAAM;OACN,YAAY;QACX,QAAQ;SACP,MAAM;SACN,QAAQ;SACR,aAAa;SACb;QACD,MAAM;SACL,MAAM;SACN,aACC;SACD;QACD,UAAU;SACT,MAAM;SACN,aAAa;SACb;QACD,cAAc;SACb,MAAM;SACN,aAAa;SACb;QACD,uBAAuB;SACtB,MAAM;SACN,QAAQ;SACR,UAAU;SACV,aAAa;SACb;QACD,mBAAmB;SAClB,MAAM;SACN,QAAQ;SACR,aAAa;SACb;QACD,kBAAkB;SACjB,MAAM;SACN,QAAQ;SACR,UAAU;SACV,aAAa;SACb;QACD,QAAQ;SACP,MAAM;SACN,OAAO,EAAE,MAAM,UAAU;SACzB,UAAU;SACV,aACC;SACD;QACD,eAAe;SACd,MAAM;SACN,QAAQ;SACR,UAAU;SACV,aAAa;SACb;QACD,6BAA6B;SAC5B,MAAM;SACN,MAAM,CAAC,sBAAsB,sBAAsB;SACnD,UAAU;SACV,aACC;SACD;QACD,cAAc;SACb,MAAM;SACN,QAAQ;SACR,UAAU;SACV,aAAa;SACb;QACD,SAAS;SACR,MAAM;SACN,UAAU;SACV,YAAY;UACX,IAAI;WACH,MAAM;WACN,aACC;WACD;UACD,OAAO;WACN,MAAM;WACN,aACC;WACD;UACD,eAAe;WACd,MAAM;WACN,UAAU;WACV,aACC;WACD;UACD,MAAM;WACL,MAAM;WACN,aACC;WACD;UACD,OAAO;WACN,MAAM;WACN,UAAU;WACV,aACC;WACD;UACD,aAAa;WACZ,MAAM;WACN,sBAAsB,EAAE,MAAM,UAAU;WACxC,UAAU;WACV,aAAa;WACb;UACD;SACD,UAAU;UAAC;UAAM;UAAS;UAAO;SACjC;QACD;OACD,UAAU;QACT;QACA;QACA;QACA;QACA;QACA;OACD,aAAa;OACb;MACD,gBAAgB;OACf,MAAM;OACN,UAAU;OACV,aAAa;OACb;MACD,QAAQ;OACP,MAAM;OACN,aACC;OACD;MACD,YAAY;OACX,MAAM;OACN,aAAa;OACb;MACD,aAAa;OACZ,MAAM;OACN,QAAQ;OACR,aACC;OACD;MACD;KACD,UAAU;MACT;MACA;MACA;MACA;MACA;MACA;MACA;KACD,EACD,EACD;IACD,EACD;GACD,EACD;EACD,EACD,OAAO,QAAQ;EACd,MAAM,OAAO,IAAI,QAAQ,SAAS;AAClC,MAAI,CAAC,KACJ,OAAM,IAAI,SAAS,eAAe;EAGnC,MAAM,QACL,OAAO,SAAS,mBAAmB,aAChC,MAAM,QAAQ,eAAe,KAAK,GACjC,SAAS,kBAAkB;AAEhC,MAAI,CAAC,MACJ,OAAM,IAAI,SAAS,aAAa,EAC/B,SAAS,yCACT,CAAC;AAQH,OALkB,MAAM,IAAI,QAAQ,QAAQ,SAAS;GACpD,OAAO;GACP,OAAO,CAAC;IAAE,OAAO;IAAU,OAAO,KAAK;IAAI,CAAC;GAC5C,CAAC,EAEY,UAAU,MACvB,OAAM,IAAI,SAAS,aAAa,EAC/B,SAAS,wDACT,CAAC;EAGH,MAAM,OAAO,IAAI;AAEjB,MADwB,EAAE,QAAQ,CAAC,KAAK,CACpB,UAAU,KAAK,OAAO,CAAC,MAC1C,OAAM,IAAI,SAAS,eAAe,EACjC,SAAS,uCACT,CAAC;AAGH,MAAI,KAAK,YAAY,aAAa,UAAU;GAC3C,MAAM,kBACL,SAAS,MAAM,mBAAmB;AACnC,OACC,IAAI,aAAa,CAAC,OAAO,KAAK,WAAW,YAAY,SAAS,CAC5D,SAAS,gBAEX,OAAM,IAAI,SAAS,eAAe,EACjC,SAAS,8CAA8C,gBAAgB,UACvE,CAAC;;AAIJ,MAAI,IAAI,KAAK,gBAcZ;OAAI,CAbiB,MAAM,IAAI,QAAQ,QAAQ,QAAQ;IACtD,OAAO;IACP,OAAO,CACN;KACC,OAAO;KACP,OAAO,KAAK;KACZ,EACD;KACC,OAAO;KACP,OAAO,IAAI,KAAK;KAChB,CACD;IACD,CAAC,CAED,OAAM,IAAI,SAAS,eAAe,EACjC,SAAS,4CACT,CAAC;;AAcJ,MAVyB,MAAM,IAAI,QAAQ,QAAQ,QAAQ;GAC1D,OAAO;GACP,OAAO,CACN;IACC,OAAO;IACP,OAAO,KAAK;IACZ,CACD;GACD,CAAC,EAEoB;AACrB,OAAI,QAAQ,OAAO,KAClB,2DAA2D,KAAK,aAChE;AACD,SAAM,IAAI,SAAS,wBAAwB,EAC1C,SAAS,oDACT,CAAC;;EAGH,IAAI,qBAAgD;AACpD,MAAI,KAAK,cAAc,CAAC,KAAK,WAAW,cACvC,KAAI;AACH,wBAAqB,MAAM,mBAAmB;IAC7C,QAAQ,KAAK;IACb,gBAAgB;KACf,mBAAmB,KAAK,WAAW;KACnC,uBAAuB,KAAK,WAAW;KACvC,eAAe,KAAK,WAAW;KAC/B,cAAc,KAAK,WAAW;KAC9B,kBAAkB,KAAK,WAAW;KAClC,6BACC,KAAK,WAAW;KACjB;IACD,kBAAkB,QAAgB,IAAI,QAAQ,gBAAgB,IAAI;IAClE,CAAC;WACM,OAAO;AACf,OAAI,iBAAiB,eACpB,OAAM,4BAA4B,MAAM;AAEzC,SAAM;;EAIR,MAAM,wBAAwB;AAC7B,OAAI,CAAC,KAAK,WAAY,QAAO;AAE7B,OAAI,KAAK,WAAW,cACnB,QAAO,KAAK,UAAU;IACrB,QAAQ,KAAK;IACb,UAAU,KAAK,WAAW;IAC1B,cAAc,KAAK,WAAW;IAC9B,uBAAuB,KAAK,WAAW;IACvC,eAAe,KAAK,WAAW;IAC/B,6BACC,KAAK,WAAW,+BAChB;IACD,cAAc,KAAK,WAAW;IAC9B,MAAM,KAAK,WAAW;IACtB,mBACC,KAAK,WAAW,qBAChB,GAAG,KAAK,OAAO;IAChB,SAAS,KAAK,WAAW;IACzB,QAAQ,KAAK,WAAW;IACxB,kBAAkB,KAAK,WAAW;IAClC,kBACC,IAAI,KAAK,oBACT,SAAS,2BACT;IACD,CAAC;AAGH,OAAI,CAAC,mBAAoB,QAAO;AAEhC,UAAO,KAAK,UAAU;IACrB,QAAQ,mBAAmB;IAC3B,UAAU,KAAK,WAAW;IAC1B,cAAc,KAAK,WAAW;IAC9B,uBAAuB,mBAAmB;IAC1C,eAAe,mBAAmB;IAClC,6BACC,mBAAmB;IACpB,cAAc,mBAAmB;IACjC,MAAM,KAAK,WAAW;IACtB,mBAAmB,mBAAmB;IACtC,SAAS,KAAK,WAAW;IACzB,QAAQ,KAAK,WAAW;IACxB,kBAAkB,mBAAmB;IACrC,kBACC,IAAI,KAAK,oBACT,SAAS,2BACT;IACD,CAAC;;AAGH,MAAI,KAAK,WACR,0BACC;GACC,oBAAoB,KAAK,WAAW;GACpC,iBAAiB,KAAK,WAAW;GACjC,EACD,SAAS,MAAM,WACf;EAGF,MAAM,WAAW,MAAM,IAAI,QAAQ,QAAQ,OAGzC;GACD,OAAO;GACP,MAAM;IACL,QAAQ,KAAK;IACb,QAAQ,KAAK;IACb,gBAAgB;IAChB,YAAY,iBAAiB;IAC7B,YAAY,KAAK,aACd,KAAK,UAAU;KACf,QAAQ,KAAK;KACb,YAAY,KAAK,WAAW;KAC5B,MAAM,KAAK,WAAW;KACtB,aAAa,KAAK,WAAW;KAC7B,UAAU,KAAK,WAAW;KAC1B,aAAa,KAAK,WAAW;KAC7B,YAAY,KAAK,WAAW;KAC5B,sBAAsB,KAAK,WAAW;KACtC,qBAAqB,KAAK,WAAW;KACrC,oBAAoB,KAAK,WAAW;KACpC,iBAAiB,KAAK,WAAW;KACjC,kBAAkB,KAAK,WAAW;KAClC,YAAY,KAAK,WAAW;KAC5B,eAAe,KAAK,WAAW;KAC/B,kBAAkB,KAAK,WAAW;KAClC,SAAS,KAAK,WAAW;KACzB,CAAC,GACD;IACH,gBAAgB,KAAK;IACrB,QAAQ,IAAI,QAAQ,QAAQ,KAAK;IACjC,YAAY,KAAK;IACjB;GACD,CAAC;EAEF,IAAI;EACJ,IAAI;AAEJ,MAAI,SAAS,oBAAoB,SAAS;AACzC,oBAAiB;AACjB,6BAA0B,qBAAqB,GAAG;AAElD,SAAM,IAAI,QAAQ,QAAQ,OAAqB;IAC9C,OAAO;IACP,MAAM;KACL,YAAY,QAAQ,oBAAoB,cACrC,GAAG,QAAQ,oBAAoB,YAAY,GAAG,SAAS,eACvD,qBAAqB,SAAS;KACjC,2BAAW,IAAI,MAAM;KACrB,2BAAW,IAAI,MAAM;KACrB,OAAO;KACP,WAAW,IAAI,KAAK,KAAK,KAAK,GAAG,OAAO,KAAK,IAAI,IAAK;KACtD;IACD,CAAC;;EAgBH,MAAM,SAAS;GACd,GAAG;GACH,YAAY,cACX,SAAS,WACT;GACD,YAAY,cACX,SAAS,WACT;GACD,aAAa,GAAG,IAAI,QAAQ,QAAQ,gBAAgB,SAAS;GAC7D,GAAI,SAAS,oBAAoB,UAAU,EAAE,gBAAgB,GAAG,EAAE;GAClE,GAAI,SAAS,oBAAoB,UAC9B,EAAE,yBAAyB,GAC3B,EAAE;GACL;AAED,SAAO,IAAI,KAAK,OAA4B;GAE7C;;AAGF,MAAM,sBAAsB,EAAE,OAAO;CACpC,OAAO,EACL,OAAO,EAAE,CAAC,CACV,KAAK,EACL,aACC,mIACD,CAAC,CACD,UAAU;CACZ,kBAAkB,EAChB,OAAO,EAAE,CAAC,CACV,KAAK,EACL,aAAa,gDACb,CAAC,CACD,UAAU;CACZ,YAAY,EACV,OAAO,EAAE,CAAC,CACV,KAAK,EACL,aACC,2FACD,CAAC,CACD,UAAU;CACZ,QAAQ,EACN,OAAO,EAAE,CAAC,CACV,KAAK,EACL,aAAa,+BACb,CAAC,CACD,UAAU;CACZ,aAAa,EAAE,OAAO,EAAE,CAAC,CAAC,KAAK,EAC9B,aAAa,sCACb,CAAC;CACF,kBAAkB,EAChB,OAAO,EAAE,CAAC,CACV,KAAK,EACL,aAAa,sCACb,CAAC,CACD,UAAU;CACZ,oBAAoB,EAClB,OAAO,EAAE,CAAC,CACV,KAAK,EACL,aAAa,yDACb,CAAC,CACD,UAAU;CACZ,QAAQ,EACN,MAAM,EAAE,QAAQ,EAAE,EAAE,CAAC,CACrB,KAAK,EACL,aAAa,wCACb,CAAC,CACD,UAAU;CACZ,WAAW,EACT,OAAO,EAAE,CAAC,CACV,KAAK,EACL,aACC,wHACD,CAAC,CACD,UAAU;CACZ,eAAe,EACb,QAAQ,EAAE,CAAC,CACX,KAAK,EACL,aACC,2FACD,CAAC,CACD,UAAU;CACZ,cAAc,EAAE,KAAK,CAAC,QAAQ,OAAO,CAAC,CAAC,UAAU;CACjD,CAAC;AAEF,MAAa,aAAa,YAAyB;AAClD,QAAO,mBACN,gBACA;EACC,QAAQ;EACR,MAAM;EACN,UAAU,EACT,SAAS;GACR,aAAa;GACb,SAAS;GACT,aACC;GACD,aAAa,EACZ,SAAS,EACR,oBAAoB,EACnB,QAAQ;IACP,MAAM;IACN,YAAY;KACX,OAAO;MACN,MAAM;MACN,aACC;MACD;KACD,QAAQ;MACP,MAAM;MACN,aACC;MACD;KACD,YAAY;MACX,MAAM;MACN,aACC;MACD;KACD,aAAa;MACZ,MAAM;MACN,aAAa;MACb;KACD,kBAAkB;MACjB,MAAM;MACN,aAAa;MACb;KACD,oBAAoB;MACnB,MAAM;MACN,aACC;MACD;KACD,WAAW;MACV,MAAM;MACN,aACC;MACD;KACD;IACD,UAAU,CAAC,cAAc;IACzB,EACD,EACD,EACD;GACD,WAAW,EACV,OAAO;IACN,aACC;IACD,SAAS,EACR,oBAAoB,EACnB,QAAQ;KACP,MAAM;KACN,YAAY;MACX,KAAK;OACJ,MAAM;OACN,QAAQ;OACR,aACC;OACD;MACD,UAAU;OACT,MAAM;OACN,aACC;OACD,MAAM,CAAC,KAAK;OACZ;MACD;KACD,UAAU,CAAC,OAAO,WAAW;KAC7B,EACD,EACD;IACD,EACD;GACD,EACD;EACD,EACD,OAAO,QAAQ;EACd,MAAM,OAAO,IAAI;EACjB,IAAI,EAAE,OAAO,kBAAkB,YAAY,WAAW;AACtD,MACC,CAAC,SAAS,YAAY,UACtB,CAAC,SACD,CAAC,oBACD,CAAC,UACD,CAAC,WAED,OAAM,IAAI,SAAS,eAAe,EACjC,SAAS,6DACT,CAAC;AAEH,WAAS,KAAK,UAAU,OAAO,MAAM,IAAI,CAAC;EAC1C,IAAI,QAAQ;AACZ,MAAI,iBACH,SAAQ,MAAM,IAAI,QAAQ,QACxB,QAAwB;GACxB,OAAO;GACP,OAAO,CACN;IACC,OAAO;IACP,OAAO;IACP,CACD;GACD,CAAC,CACD,MAAM,QAAQ;AACd,OAAI,CAAC,IACJ,QAAO;AAER,UAAO,IAAI;IACV;EAEJ,IAAI,WAA2C;AAC/C,MAAI,SAAS,YAAY,QAAQ;GAEhC,MAAM,kBAAkB,aACrB,QAAQ,WAAW,MAClB,oBAAoB,gBAAgB,eAAe,WACpD,GACA,QAAQ,WAAW,MAClB,oBAAoB,gBAAgB,WAAW,OAChD;AAEH,OAAI,gBACH,YAAW;IACV,QACC,gBAAgB,YAAY,UAC5B,gBAAgB,YAAY,UAC5B;IACD,YAAY,gBAAgB;IAC5B,QAAQ;IACR,YAAY,gBAAgB;IAC5B,YAAY,gBAAgB;IAC5B,QAAQ,gBAAgB;IACxB,GAAI,QAAQ,oBAAoB,UAC7B,EAAE,gBAAgB,MAAM,GACxB,EAAE;IACL;;AAGH,MAAI,CAAC,cAAc,CAAC,SAAS,CAAC,OAC7B,OAAM,IAAI,SAAS,eAAe,EACjC,SAAS,2CACT,CAAC;AAGH,MAAI,CAAC,UAAU;GACd,MAAM,iBAAiB,QAAwC;AAC9D,QAAI,CAAC,IAAK,QAAO;AACjB,WAAO;KACN,GAAG;KACH,YAAY,IAAI,aACb,cACA,IAAI,WACJ,IAAI,SACJ;KACH,YAAY,IAAI,aACb,cACA,IAAI,WACJ,IAAI,SACJ;KACH;;AAGF,OAAI,cAAc,MAEjB,YAAW,cACV,MAAM,IAAI,QAAQ,QAAQ,QAAiC;IAC1D,OAAO;IACP,OAAO,CACN;KACC,OAAO,aAAa,eAAe;KACnC,OAAO,cAAc;KACrB,CACD;IACD,CAAC,CACF;YACS,QAAQ;AAGlB,eAAW,cACV,MAAM,IAAI,QAAQ,QAAQ,QAAiC;KAC1D,OAAO;KACP,OAAO,CAAC;MAAE,OAAO;MAAU,OAAO;MAAQ,CAAC;KAC3C,CAAC,CACF;AAED,QAAI,CAAC,SASJ,YAAW,eARU,MAAM,IAAI,QAAQ,QAAQ,SAE7C,EACD,OAAO,eACP,CAAC,EACoC,MAAM,MAC3C,cAAc,QAAQ,EAAE,OAAO,CAC/B,IAC4C,KAAK;;;AAKrD,MAAI,CAAC,SACJ,OAAM,IAAI,SAAS,aAAa,EAC/B,SAAS,oCACT,CAAC;AAGH,MAAI,KAAK,cAAc;AACtB,OAAI,KAAK,iBAAiB,UAAU,CAAC,SAAS,WAC7C,OAAM,IAAI,SAAS,eAAe,EACjC,SAAS,mCACT,CAAC;AAEH,OAAI,KAAK,iBAAiB,UAAU,CAAC,SAAS,WAC7C,OAAM,IAAI,SAAS,eAAe,EACjC,SAAS,mCACT,CAAC;;AAIJ,MACC,SAAS,oBAAoB,WAC7B,EAAE,oBAAoB,YAAY,SAAS,gBAE3C,OAAM,IAAI,SAAS,gBAAgB,EAClC,SAAS,yCACT,CAAC;AAGH,MAAI,SAAS,cAAc,KAAK,iBAAiB,QAAQ;GACxD,IAAI,eAAe,SAAS,WAAW;AACvC,OAAI,CAAC,gBAAgB,SAAS,WAAW,mBAAmB;IAC3D,MAAM,YAAY,MAAM,YAErB,SAAS,WAAW,mBAAmB,EACzC,QAAQ,OACR,CAAC;AACF,QAAI,UAAU,KACb,gBAAe,UAAU,KAAK;;AAGhC,OAAI,CAAC,aACJ,OAAM,IAAI,SAAS,eAAe,EACjC,SAAS,4DACT,CAAC;GAEH,MAAM,QAAQ,MAAM,cAAc,KAAK,QAAW,MAAM;GACxD,MAAM,cAAc,GAAG,IAAI,QAAQ,QAAQ,gBAAgB,SAAS;GACpE,MAAM,mBAAmB,MAAM,uBAAuB;IACrD,IAAI,SAAS;IACb,SAAS;KACR,UAAU,SAAS,WAAW;KAC9B,cAAc,SAAS,WAAW;KAClC;IACD;IACA,OAAO,MAAM;IACb,cAAc,SAAS,WAAW,OAC/B,MAAM,eACN;IACH,QAAQ,IAAI,KAAK,UAChB,SAAS,WAAW,UAAU;KAC7B;KACA;KACA;KACA;KACA;IACF,WAAW,IAAI,KAAK,aAAa;IACjC,uBAAuB;IACvB,CAAC;AACF,UAAO,IAAI,KAAK;IACf,KAAK,iBAAiB,UAAU;IAChC,UAAU;IACV,CAAC;;AAEH,MAAI,SAAS,YAAY;GACxB,MAAM,mBACL,OAAO,SAAS,eAAe,WAC5B,SAAS,aACT,cACA,SAAS,WACT;AACJ,OAAI,CAAC,iBACJ,OAAM,IAAI,SAAS,eAAe,EACjC,SAAS,8BACT,CAAC;AAGH,OACC,iBAAiB,uBACjB,CAAC,iBAAiB,YAAY,cAC9B,CAAC,iBAAiB,WAElB,KAAI,QAAQ,OAAO,KAClB,gGACA,EAAE,YAAY,SAAS,YAAY,CACnC;GAGF,IAAI,WAAW,iBAAiB,WAAW;AAE3C,OAAI,CAAC,SACJ,YACC,KACE,WAAW;IACX,UACC,iBAAiB,YAAY,YAC7B,iBAAiB;IAClB,0BAA0B,CACzB;KACC,SAAS;KACT,UACC,iBAAiB,eACjB,GAAG,IAAI,QAAQ,QAAQ,oBAAoB,SAAS;KACrD,CACD;IACD,mBACC,iBAAiB,wBAAwB;IAC1C,qBACC,iBAAiB,uBAAuB;IACzC,cAAc,iBAAiB,mBAC5B,CAAC,iBAAiB,iBAAiB,GACnC;IACH,CAAC,CACD,aAAa,IAAI;GAGrB,MAAM,KAAK,KAAK,gBAAgB;IACrB;IACV,aAAa;IACb,YACC,iBAAiB,YAAY,cAC7B,iBAAiB;IAClB,gBAAgB,iBAAiB,YAAY;IAC7C,CAAC;GAEF,MAAM,MAAM,KAAK,iBAAiB;IACjC,UAAU,iBAAiB,aAAa;IACxC,UAAU,iBAAiB,aAAa;IACxC,aAAa,iBAAiB,aAAa;IAC3C,qBACC,iBAAiB,aAAa;IAC/B,CAAC;GACF,MAAM,eAAe,GAAG,mBACvB,KACA,WACA;AAKD,OAAI,CAAC,aACJ,OAAM,IAAI,SAAS,eAAe,EACjC,SAAS,wBACT,CAAC;GAGH,MAAM,EAAE,OAAO,eAAe,MAAM,mBACnC,KACA,QACA,MACA;AAID,OADC,aAAa,MAAM,SAAS,MAAM,8BACZ;IACtB,MAAM,MAAM,SAAS,MAAM,cAAc;IACzC,MAAM,SAA6B;KAClC,IAAI,aAAa;KACjB,YAAY,SAAS;KACrB,WAAW,KAAK,KAAK;KACrB,WAAW,KAAK,KAAK,GAAG;KACxB;AACD,UAAM,IAAI,QAAQ,gBAAgB,wBAAwB;KACzD,YAAY,GAAG,2BAA2B,OAAO;KACjD,OAAO,KAAK,UAAU,OAAO;KAC7B,WAAW,IAAI,KAAK,OAAO,UAAU;KACrC,CAAC;;AAGH,UAAO,IAAI,KAAK;IACf,KAAK,GAAG,aAAa,QAAQ,cAAc,mBAAmB,WAAW;IACzE,UAAU;IACV,CAAC;;AAEH,QAAM,IAAI,SAAS,eAAe,EACjC,SAAS,wBACT,CAAC;GAEH;;AAGF,MAAM,yBAAyB,EAAE,OAAO;CACvC,MAAM,EAAE,QAAQ,CAAC,UAAU;CAC3B,OAAO,EAAE,QAAQ;CACjB,OAAO,EAAE,QAAQ,CAAC,UAAU;CAC5B,mBAAmB,EAAE,QAAQ,CAAC,UAAU;CACxC,CAAC;AAEF,MAAa,eAAe,YAAyB;AACpD,QAAO,mBACN,6BACA;EACC,QAAQ;EACR,OAAO;EACP,mBAAmB,CAClB,qCACA,mBACA;EACD,UAAU;GACT,GAAG;GACH,SAAS;IACR,aAAa;IACb,SAAS;IACT,aACC;IACD,WAAW,EACV,OAAO,EACN,aAAa,iCACb,EACD;IACD;GACD;EACD,EACD,OAAO,QAAQ;EACd,MAAM,EAAE,MAAM,OAAO,sBAAsB,IAAI;EAC/C,MAAM,YAAY,MAAM,WAAW,IAAI;AACvC,MAAI,CAAC,WAAW;GACf,MAAM,WACL,IAAI,QAAQ,QAAQ,YAAY,YAChC,GAAG,IAAI,QAAQ,QAAQ;AACxB,SAAM,IAAI,SAAS,GAAG,SAAS,sBAAsB;;EAEtD,MAAM,EAAE,aAAa,UAAU,YAAY,kBAAkB;AAC7D,MAAI,CAAC,QAAQ,MACZ,OAAM,IAAI,SACT,GACC,YAAY,YACZ,SAAS,MAAM,qBAAqB,oBACrC;EAEF,IAAI,WAA2C;AAC/C,MAAI,SAAS,YAAY,QAAQ;GAChC,MAAM,kBAAkB,QAAQ,WAAW,MACzC,oBACA,gBAAgB,eAAe,IAAI,OAAO,WAC3C;AACD,OAAI,gBACH,YAAW;IACV,GAAG;IACH,QAAQ,gBAAgB,YAAY,UAAU;IAC9C,QAAQ;IACR,GAAI,QAAQ,oBAAoB,UAC7B,EAAE,gBAAgB,MAAM,GACxB,EAAE;IACL;;AAGH,MAAI,CAAC,SACJ,YAAW,MAAM,IAAI,QAAQ,QAC3B,QAEE;GACF,OAAO;GACP,OAAO,CACN;IACC,OAAO;IACP,OAAO,IAAI,OAAO;IAClB,CACD;GACD,CAAC,CACD,MAAM,QAAQ;AACd,OAAI,CAAC,IACJ,QAAO;AAER,UAAO;IACN,GAAG;IACH,YACC,cAA0B,IAAI,WAAW,IAAI;IAC9C;IACA;AAEJ,MAAI,CAAC,SACJ,OAAM,IAAI,SACT,GACC,YAAY,YACZ,oEACD;AAGF,MACC,SAAS,oBAAoB,WAC7B,EAAE,oBAAoB,YAAY,SAAS,gBAE3C,OAAM,IAAI,SAAS,gBAAgB,EAClC,SAAS,yCACT,CAAC;EAGH,IAAI,SAAS,SAAS;AAEtB,MAAI,CAAC,OACJ,OAAM,IAAI,SACT,GACC,YAAY,YACZ,oEACD;EAGF,MAAM,YAAY,MAAM,YAMrB,OAAO,kBAAkB;AAE5B,MAAI,UAAU,KACb,UAAS;GACR,eAAe,UAAU,KAAK;GAC9B,6BACC,UAAU,KAAK;GAChB,kBAAkB,UAAU,KAAK;GACjC,QAAQ;IAAC;IAAU;IAAS;IAAW;IAAiB;GACxD,GAAG;GACH;AAGF,MAAI,CAAC,OAAO,cACX,OAAM,IAAI,SACT,GACC,YAAY,YACZ,0EACD;EAGF,MAAM,gBAAgB,MAAM,0BAA0B;GACrD;GACA,cAAc,OAAO,OAAO,UAAU,eAAe;GACrD,aAAa,GAAG,IAAI,QAAQ,QAAQ,gBAAgB,SAAS;GAC7D,SAAS;IACR,UAAU,OAAO;IACjB,cAAc,OAAO;IACrB;GACD,eAAe,OAAO;GACtB,gBACC,OAAO,gCAAgC,uBACpC,SACA;GACJ,CAAC,CAAC,OAAO,MAAM;AACf,OAAI,aAAa,iBAChB,OAAM,IAAI,SACT,GACC,YAAY,YACZ,4CAA4C,EAAE,UAC/C;AAEF,UAAO;IACN;AACF,MAAI,CAAC,cACJ,OAAM,IAAI,SACT,GACC,YAAY,YACZ,0EACD;EAEF,IAAI,WAOO;AACX,MAAI,cAAc,SAAS;GAC1B,MAAM,UAAU,UAAU,cAAc,QAAQ;AAChD,OAAI,CAAC,OAAO,aACX,OAAM,IAAI,SACT,GACC,YAAY,YACZ,yEACD;GAEF,MAAM,WAAW,MAAM,cACtB,cAAc,SACd,OAAO,aACP,CAAC,OAAO,MAAM;AACd,QAAI,QAAQ,OAAO,MAAM,EAAE;AAC3B,WAAO;KACN;AACF,OAAI,CAAC,SACJ,OAAM,IAAI,SACT,GACC,YAAY,YACZ,oEACD;AAEF,OAAI,SAAS,QAAQ,QAAQ,SAAS,OACrC,OAAM,IAAI,SACT,GACC,YAAY,YACZ,iEACD;GAGF,MAAM,UAAU,OAAO,WAAW,EAAE;AACpC,cAAW;IACV,GAAG,OAAO,YACT,OAAO,QAAQ,QAAQ,eAAe,EAAE,CAAC,CAAC,KAAK,CAAC,KAAK,WAAW,CAC/D,KACA,SAAS,QAAQ,OACjB,CAAC,CACF;IACD,IAAI,QAAQ,QAAQ,MAAM;IAC1B,OAAO,QAAQ,QAAQ,SAAS;IAChC,eAAe,SAAS,qBACrB,QAAQ,QAAQ,iBAAiB,oBACjC;IACH,MAAM,QAAQ,QAAQ,QAAQ;IAC9B,OAAO,QAAQ,QAAQ,SAAS;IAChC;;AASF,MAAI,CAAC,UAAU;AACd,OAAI,CAAC,OAAO,iBACX,OAAM,IAAI,SACT,GACC,YAAY,YACZ,8EACD;GAEF,MAAM,mBAAmB,MAAM,YAM5B,OAAO,kBAAkB,EAC3B,SAAS,EACR,eAAe,UAAU,cAAc,eACvC,EACD,CAAC;AACF,OAAI,iBAAiB,MACpB,OAAM,IAAI,SACT,GACC,YAAY,YACZ,kDACA,iBAAiB,MAAM,UAExB;AAEF,cAAW,iBAAiB;;AAG7B,MAAI,CAAC,SAAS,SAAS,CAAC,SAAS,GAChC,OAAM,IAAI,SACT,GACC,YAAY,YACZ,mEACD;EAEF,MAAM,oBACL,oBAAoB,YACnB,SAA0C,mBAAmB,QAC9D,oBAAoB,SAAS,OAAO,SAAS,OAAO;EAErD,MAAM,SAAS,MAAM,oBAAoB,KAAK;GAC7C,UAAU;IACT,OAAO,SAAS;IAChB,MAAM,SAAS,QAAQ,SAAS;IAChC,IAAI,SAAS;IACb,OAAO,SAAS;IAChB,eAAe,SAAS,qBACrB,SAAS,iBAAiB,QAC1B;IACH;GACD,SAAS;IACR,SAAS,cAAc;IACvB,aAAa,cAAc;IAC3B,cAAc,cAAc;IAC5B,WAAW,SAAS;IACpB,YAAY,SAAS;IACrB,sBAAsB,cAAc;IACpC,uBAAuB,cAAc;IACrC,OAAO,cAAc,QAAQ,KAAK,IAAI;IACtC;GACD;GACA,eAAe,SAAS,yBAAyB,CAAC;GAClD,kBAAkB,OAAO;GACzB;GACA,CAAC;AACF,MAAI,OAAO,MACV,OAAM,IAAI,SACT,GAAG,YAAY,YAAY,eAAe,OAAO,QACjD;EAEF,MAAM,EAAE,SAAS,SAAS,OAAO;AAEjC,MAAI,SAAS,cACZ,OAAM,QAAQ,cAAc;GAC3B;GACA;GACA,OAAO;GACP;GACA,CAAC;AAGH,QAAM,+BAA+B,KAAY;GAChD;GACA,SAAS;IACR,cAAc;IACd,YAAY,SAAS;IACrB,WAAW,SAAS;IACpB,OAAO,SAAS;IAChB,eAAe,QAAQ,SAAS,cAAc;IAC9C,eAAe;IACf;GACD;GACA,OAAO;GACP,qBAAqB,SAAS;GAC9B,CAAC;AAEF,QAAM,iBAAiB,KAAK;GAC3B;GACA;GACA,CAAC;EACF,IAAI;AACJ,MAAI;AAEH,mBADY,OAAO,aAAa,cAAc,cAAc,aACzC,UAAU;UACtB;AACP,kBAAe,OAAO,aACnB,cAAc,cACd;;AAEJ,QAAM,IAAI,SAAS,aAAa;GAEjC;;AAGF,MAAM,4BAA4B,EAAE,OAAO;CAC1C,cAAc,EAAE,QAAQ;CACxB,YAAY,EAAE,QAAQ,CAAC,UAAU;CACjC,CAAC;;;;;;;AAQF,MAAM,sBACL,KACA,cACA,WACA,oBAIY;AACZ,KAAI,CAAC,IACJ,QAAO;AAGR,KAAI,IAAI,WAAW,IAAI,IAAI,CAAC,IAAI,WAAW,KAAK,EAAE;AACjD,MAAI;GACH,MAAM,cAAc,IAAI,IAAI,KAAK,UAAU;AAC3C,OAAI,YAAY,WAAW,UAC1B,QAAO;GAER,MAAM,mBAAmB,IAAI,IAAI,aAAa,CAAC;AAC/C,OAAI,YAAY,aAAa,iBAC5B,QAAO;UAED;AACP,UAAO;;AAER,SAAO;;AAGR,KAAI,CAAC,gBAAgB,KAAK,EAAE,oBAAoB,OAAO,CAAC,CACvD,QAAO;AAGR,KAAI;EACH,MAAM,mBAAmB,IAAI,IAAI,aAAa,CAAC;AAE/C,MADoB,IAAI,IAAI,IAAI,CAAC,aACb,iBACnB,QAAO;SAED;AACP,MAAI,QAAQ,gBAAgB,IAAI,WAAW,GAAG,aAAa,GAAG,CAC7D,QAAO;;AAIT,QAAO;;AAGR,MAAa,mBAAmB,YAAyB;AACxD,QAAO,mBACN,mCACA;EACC,QAAQ,CAAC,OAAO,OAAO;EACvB,MAAM,0BAA0B,UAAU;EAC1C,OAAO,EACL,OAAO,EACP,YAAY,EAAE,QAAQ,CAAC,UAAU,EACjC,CAAC,CACD,UAAU;EACZ,UAAU;GACT,GAAG;GACH,mBAAmB,CAClB,qCACA,mBACA;GACD,SAAS;IACR,aAAa;IACb,SAAS;IACT,aACC;IACD,WAAW;KACV,OAAO,EACN,aAAa,iCACb;KACD,OAAO,EACN,aAAa,yBACb;KACD,OAAO,EACN,aAAa,6CACb;KACD;IACD;GACD;EACD,EACD,OAAO,QAAQ;EACd,MAAM,EAAE,eAAe,IAAI;EAC3B,MAAM,YAAY,IAAI,IAAI,IAAI,QAAQ,QAAQ,CAAC;EAC/C,MAAM,WACL,IAAI,QAAQ,QAAQ,YAAY,YAAY,GAAG,UAAU;EAC1D,MAAM,sBAAsB,GAAG,IAAI,QAAQ,QAAQ,sBAAsB;AAMzE,MAFqB,IAAI,WAAW,SAAS,CAAC,IAAI,MAAM,cAEtC;AAGjB,OAAI,EAFY,MAAM,kBAAkB,IAAI,GAE9B,QACb,OAAM,IAAI,SAAS,GAAG,SAAS,wBAAwB;GAGxD,MAAM,aAAa,IAAI,OAAO;GAC9B,MAAM,kBAAkB,mBACvB,YACA,qBACA,YACC,KAAK,aAAa,IAAI,QAAQ,gBAAgB,KAAK,SAAS,CAC7D;AAED,SAAM,IAAI,SAAS,gBAAgB;;AAGpC,MAAI,CAAC,IAAI,MAAM,aACd,OAAM,IAAI,SAAS,eAAe,EACjC,SAAS,8CACT,CAAC;EAGH,MAAM,EAAE,iBAAiB,IAAI;EAE7B,MAAM,kBACL,SAAS,MAAM,mBAAmB;AACnC,MAAI,IAAI,aAAa,CAAC,OAAO,aAAa,CAAC,SAAS,gBACnD,OAAM,IAAI,SAAS,eAAe,EACjC,SAAS,+CAA+C,gBAAgB,UACxE,CAAC;EAGH,IAAI,aAAgC;AACpC,MAAI,IAAI,KAAK,WACZ,KAAI;AACH,gBAAa,MAAM,gBAAgB,IAAI;UAChC;AACP,gBAAa;;EAGf,IAAI,WAA2C;AAC/C,MAAI,SAAS,YAAY,QAAQ;GAChC,MAAM,kBAAkB,QAAQ,WAAW,MACzC,oBAAoB,gBAAgB,eAAe,WACpD;AACD,OAAI,gBACH,YAAW;IACV,GAAG;IACH,QAAQ;IACR,QAAQ,gBAAgB,YAAY,UAAU;IAC9C,GAAI,QAAQ,oBAAoB,UAC7B,EAAE,gBAAgB,MAAM,GACxB,EAAE;IACL;;AAGH,MAAI,CAAC,SACJ,YAAW,MAAM,IAAI,QAAQ,QAC3B,QAAiC;GACjC,OAAO;GACP,OAAO,CAAC;IAAE,OAAO;IAAc,OAAO;IAAY,CAAC;GACnD,CAAC,CACD,MAAM,QAAQ;AACd,OAAI,CAAC,IAAK,QAAO;AACjB,UAAO;IACN,GAAG;IACH,YAAY,IAAI,aACb,cACA,IAAI,WACJ,IAAI,SACJ;IACH;IACA;AAGJ,MAAI,CAAC,SACJ,OAAM,IAAI,SAAS,aAAa,EAC/B,SAAS,8CACT,CAAC;AAGH,MACC,SAAS,oBAAoB,WAC7B,EAAE,oBAAoB,YAAY,SAAS,gBAE3C,OAAM,IAAI,SAAS,gBAAgB,EAClC,SAAS,yCACT,CAAC;EAGH,MAAM,mBAAmB,cACxB,SAAS,WACT;AACD,MAAI,CAAC,iBACJ,OAAM,IAAI,SAAS,eAAe,EACjC,SAAS,8BACT,CAAC;EAEH,MAAM,UAAU,iBAAiB;EACjC,IAAI,MAA+B;AAGnC,MAAI,CAAC,SAAS,SACb,OAAM,KAAK,iBAAiB;GAC3B,UAAU,SAAS,YAAY,iBAAiB;GAChD,qBAAqB,CACpB;IACC,SAAS;IACT,UAAU,iBAAiB;IAC3B,CACD;GACD,aAAa,SAAS,QAAQ,iBAAiB;GAC/C,yBACC,iBAAiB,wBAAwB;GAC1C,sBAAsB,SAAS,wBAAwB;GACvD,eAAe,SAAS;GACxB,mBAAmB,SAAS;GAC5B,CAAC;MAEF,OAAM,KAAK,iBAAiB;GAC3B,UAAU,QAAQ;GAClB,YAAY,QAAQ;GACpB,gBAAgB,QAAQ;GACxB,sBAAsB,QAAQ;GAC9B,eAAe,QAAQ;GACvB,mBAAmB,QAAQ;GAC3B,CAAC;EAIH,MAAM,SAAS,iBAAiB;EAChC,MAAM,KAAK,KAAK,gBAAgB;GAC/B,UAAU,QAAQ;GAClB,UAAU,QAAQ,YAAY,iBAAiB;GAC/C,0BAA0B,QAAQ,WAC/B,SACA,CACA;IACC,SAAS;IACT,UAAU,iBAAiB;IAC3B,CACD;GACH,YAAY,QAAQ,cAAc,iBAAiB;GACnD,gBAAgB,QAAQ;GACxB,sBAAsB,QAAQ,wBAAwB;GACtD,eAAe,QAAQ;GACvB,mBAAmB,QAAQ;GAC3B,mBAAmB,iBAAiB,wBAAwB;GAC5D,cAAc,iBAAiB,mBAC5B,CAAC,iBAAiB,iBAAiB,GACnC;GACH,CAAC;AAEF,0BAAwB,aAAa;EAErC,IAAI;AACJ,MAAI;AACH,oBAAiB,MAAM,GAAG,mBAAmB,KAAK,QAAQ,EACzD,MAAM;IACL;IACA,YAAY,IAAI,KAAK,cAAc;IACnC,EACD,CAAC;AAEF,OAAI,CAAC,gBAAgB,QACpB,OAAM,IAAI,MAAM,kCAAkC;WAE3C,OAAO;AACf,OAAI,QAAQ,OAAO,MAAM,mCAAmC;IAC3D;IACA,iBAAiB,OAAO,KAAK,cAAc,SAAS,CAAC,SACpD,QACA;IACD,CAAC;AACF,SAAM,IAAI,SAAS,eAAe;IACjC,SAAS;IACT,SAAS,iBAAiB,QAAQ,MAAM,UAAU,OAAO,MAAM;IAC/D,CAAC;;EAGH,MAAM,EAAE,YAAY;AAEpB,yBAAuB,gBAAgB,SAAS,MAAM,WAAW;AAEjE,wBAAuB,QAAgB,YAAY;GAClD,WAAW,SAAS,MAAM;GAC1B,mBAAmB,SAAS,MAAM;GAClC,QAAQ,IAAI,QAAQ;GACpB,CAAC;EAEF,MAAM,eAAgB,QAAgB;AAItC,MAFC,SAAS,MAAM,8BAEgB;GAC/B,MAAM,oBAAoB,SAAS,MAAM,sBAAsB;AAE/D,OAAI,cAAc;IACjB,IAAI,gBAA2C;IAE/C,MAAM,eACL,MAAM,IAAI,QAAQ,gBAAgB,sBACjC,GAAG,2BAA2B,eAC9B;AACF,QAAI,aACH,KAAI;AACH,qBAAgB,KAAK,MACpB,aAAa,MACb;AACD,SAAI,iBAAiB,cAAc,YAAY,KAAK,KAAK,CACxD,iBAAgB;YAEV;AACP,qBAAgB;;AAIlB,QAAI,CAAC,eAAe;AACnB,SAAI,QAAQ,OAAO,MAClB,sEACA;MAAE;MAAc,YAAY,SAAS;MAAY,CACjD;KACD,MAAM,cACL,YAAY,eACZ,iBAAiB,eACjB,IAAI,QAAQ;AACb,WAAM,IAAI,SACT,GAAG,YAAY,8EACf;;AAGF,QAAI,cAAc,eAAe,SAAS,YAAY;AACrD,SAAI,QAAQ,OAAO,MAClB,0DACA;MACC;MACA,kBAAkB,cAAc;MAChC,gBAAgB,SAAS;MACzB,CACD;AAED,WAAM,IAAI,QAAQ,gBAAgB,+BACjC,GAAG,2BAA2B,eAC9B;KACD,MAAM,cACL,YAAY,eACZ,iBAAiB,eACjB,IAAI,QAAQ;AACb,WAAM,IAAI,SACT,GAAG,YAAY,kEACf;;AAGF,UAAM,IAAI,QAAQ,gBAAgB,+BACjC,GAAG,2BAA2B,eAC9B;cACS,CAAC,mBAAmB;AAC9B,QAAI,QAAQ,OAAO,MAClB,wFACA,EAAE,YAAY,SAAS,YAAY,CACnC;IACD,MAAM,cACL,YAAY,eACZ,iBAAiB,eACjB,IAAI,QAAQ;AACb,UAAM,IAAI,SACT,GAAG,YAAY,6EACf;;;EAKH,MAAM,cAAe,eAAuB;EAG5C,MAAM,cAAc,cAAc,mBAAmB,YAAY,GAAG;AAEpE,MAAI,aAAa;GAChB,MAAM,SAAS,IAAI,WAAW,aAAa;GAC3C,MAAM,aAAc,QAAgB;GAGpC,MAAM,YAAY,SAAS,MAAM,aAAa;GAC9C,MAAM,YAAY,YAAY,eAC3B,IAAI,KAAK,WAAW,aAAa,CAAC,SAAS,GAAG,YAC9C,KAAK,KAAK,GAAG;GAEhB,MAAM,oBACL,MAAM,IAAI,QAAQ,gBAAgB,sBACjC,GAAG,4BAA4B,cAC/B;GAEF,IAAI,WAAW;AACf,OAAI,kBACH,KAAI;AAEH,QADe,KAAK,MAAM,kBAAkB,MAAM,CACvC,aAAa,KAAK,KAAK,CACjC,YAAW;YAEJ,OAAO;AACf,QAAI,QAAQ,OAAO,KAAK,2CAA2C;KAClE;KACA;KACA,CAAC;;AAIJ,OAAI,UAAU;AACb,QAAI,QAAQ,OAAO,MAClB,6DACA;KACC;KACA;KACA,YAAY,SAAS;KACrB,CACD;IACD,MAAM,cACL,YAAY,eACZ,iBAAiB,eACjB,IAAI,QAAQ;AACb,UAAM,IAAI,SACT,GAAG,YAAY,+EACf;;AAGF,SAAM,IAAI,QAAQ,gBAAgB,wBAAwB;IACzD,YAAY,GAAG,4BAA4B;IAC3C,OAAO,KAAK,UAAU;KACrB;KACA;KACA,YAAY,SAAS;KACrB,QAAQ,KAAK,KAAK;KAClB;KACA,CAAC;IACF,WAAW,IAAI,KAAK,UAAU;IAC9B,CAAC;QAEF,KAAI,QAAQ,OAAO,KAClB,wDACA,EAAE,YAAY,SAAS,YAAY,CACnC;EAGF,MAAM,aAAa,QAAQ,cAAc,EAAE;EAC3C,MAAM,UAAU,iBAAiB,WAAW,EAAE;EAE9C,MAAM,WAAW;GAChB,GAAG,OAAO,YACT,OAAO,QAAQ,QAAQ,eAAe,EAAE,CAAC,CAAC,KAAK,CAAC,KAAK,WAAW,CAC/D,KACA,WAAW,OACX,CAAC,CACF;GACD,IAAI,WAAW,QAAQ,MAAM,aAAa,QAAQ;GAClD,QACC,WAAW,QAAQ,SAAS,YAAY,QAAQ,QAC/C,aAAa;GACf,MACC,CACC,WAAW,QAAQ,aAAa,cAChC,WAAW,QAAQ,YAAY,WAC/B,CACC,OAAO,QAAQ,CACf,KAAK,IAAI,IACX,WAAW,QAAQ,QAAQ,kBAC3B,QAAQ;GACT,eACC,SAAS,sBAAsB,QAAQ,gBAClC,WAAW,QAAQ,kBAAkB,QACvC;GACJ;AACD,MAAI,CAAC,SAAS,MAAM,CAAC,SAAS,OAAO;AACpC,OAAI,QAAQ,OAAO,MAClB,kDACA;IACC,YAAY,OAAO,KAAK,WAAW;IACnC;IACA,aAAa,SAAS;IACtB,gBAAgB,SAAS;IACzB,CACD;AACD,SAAM,IAAI,SAAS,eAAe,EACjC,SAAS,yDACT,CAAC;;EAGH,MAAM,oBACL,CAAC,CAAC,IAAI,QAAQ,QAAQ,SAAS,gBAAgB,kBAAkB,SAChE,SAAS,WACT,IACA,oBAAoB,YACpB,CAAC,CAAE,SAA0C,kBAC7C,oBAAoB,SAAS,OAAiB,SAAS,OAAO;EAEhE,MAAM,cACL,YAAY,eACZ,iBAAiB,eACjB,IAAI,QAAQ;EAEb,MAAM,SAAS,MAAM,oBAAoB,KAAK;GAC7C,UAAU;IACT,OAAO,SAAS;IAChB,MAAO,SAAS,QAAQ,SAAS;IACjC,IAAI,SAAS;IACb,eAAe,QAAQ,SAAS,cAAc;IAC9C;GACD,SAAS;IACR,YAAY,SAAS;IACrB,WAAW,SAAS;IACpB,aAAa;IACb,cAAc;IACd;GACD,aAAa;GACb,eAAe,SAAS;GACxB;GACA,CAAC;AAEF,MAAI,OAAO,MACV,OAAM,IAAI,SACT,GAAG,YAAY,SAAS,OAAO,MAAM,MAAM,IAAI,CAAC,KAAK,IAAI,GACzD;EAGF,MAAM,EAAE,SAAS,SAAS,OAAO;AAEjC,MAAI,SAAS,cACZ,OAAM,QAAQ,cAAc;GACrB;GACN;GACA;GACA,CAAC;AAGH,QAAM,+BAA+B,KAAY;GAChD;GACA,SAAS;IACR,cAAc;IACd,YAAY,SAAS;IACrB,WAAW,SAAS;IACpB,OAAO,SAAS;IAChB,eAAe,QAAQ,SAAS,cAAc;IAC9C,eAAe;IACf;GACD;GACA,qBAAqB,SAAS;GAC9B,CAAC;AAEF,QAAM,iBAAiB,KAAK;GAAE;GAAS;GAAM,CAAC;EAE9C,MAAM,kBAAkB,mBACvB,YAAY,eAAe,iBAAiB,aAC5C,qBACA,YACC,KAAK,aAAa,IAAI,QAAQ,gBAAgB,KAAK,SAAS,CAC7D;AACD,QAAM,IAAI,SAAS,gBAAgB;GAEpC;;AAGF,MAAM,wBAAwB,EAAE,OAAO;CACtC,cAAc,EAAE,QAAQ;CACxB,YAAY,EAAE,QAAQ,CAAC,UAAU;CACjC,CAAC;AAEF,MAAa,eAAe,YAAyB;AACpD,QAAO,mBACN,iCACA;EACC,QAAQ;EACR,MAAM;EACN,UAAU;GACT,GAAG;GACH,mBAAmB,CAClB,qCACA,mBACA;GACD,SAAS;IACR,aAAa;IACb,SAAS;IACT,aACC;IACD,WAAW,EACV,OAAO,EACN,aACC,iEACD,EACD;IACD;GACD;EACD,EACD,OAAO,QAAQ;EACd,MAAM,EAAE,cAAc,aAAa,OAAO,IAAI;EAC9C,MAAM,EAAE,eAAe,IAAI;EAE3B,MAAM,kBACL,SAAS,MAAM,mBAAmB;AACnC,MAAI,IAAI,aAAa,CAAC,OAAO,aAAa,CAAC,SAAS,gBACnD,OAAM,IAAI,SAAS,eAAe,EACjC,SAAS,+CAA+C,gBAAgB,UACxE,CAAC;EAIH,IAAI,WAA2C;AAE/C,MAAI,SAAS,YAAY,QAAQ;GAEhC,MAAM,kBAAkB,aACrB,QAAQ,WAAW,MAClB,oBAAoB,gBAAgB,eAAe,WACpD,GACA,QAAQ,WAAW;AAEtB,OAAI,gBACH,YAAW;IACV,QAAQ,gBAAgB,YAAY,UAAU;IAC9C,YAAY,gBAAgB;IAC5B,QAAQ;IACR,YAAY,gBAAgB;IAC5B,QAAQ,gBAAgB;IACxB,GAAI,QAAQ,oBAAoB,UAC7B,EAAE,gBAAgB,MAAM,GACxB,EAAE;IACL;QAGF,YAAW,MAAM,IAAI,QAAQ,QAC3B,QAAiC;GACjC,OAAO;GACP,OAAO,CACN;IACC,OAAO;IACP,OAAO;IACP,CACD;GACD,CAAC,CACD,MAAM,QAAQ;AACd,OAAI,CAAC,IAAK,QAAO;AACjB,UAAO;IACN,GAAG;IACH,YAAY,IAAI,aACb,cACA,IAAI,WACJ,IAAI,SACJ;IACH;IACA;AAGJ,MAAI,CAAC,UAAU,WACd,OAAM,IAAI,SAAS,aAAa,EAC/B,SAAS,0BACT,CAAC;AAGH,MACC,SAAS,oBAAoB,WAC7B,EAAE,oBAAoB,YAAY,SAAS,gBAE3C,OAAM,IAAI,SAAS,gBAAgB,EAClC,SAAS,yCACT,CAAC;EAGH,MAAM,mBAAmB,SAAS;EAElC,MAAM,KAAK,KAAK,gBAAgB;GAC/B,UACC,iBAAiB,YAAY,YAAY,iBAAiB;GAC3D,0BAA0B,CACzB;IACC,SAAS;IACT,UACC,iBAAiB,eACjB,GAAG,IAAI,QAAQ,QAAQ,oBAAoB;IAC5C,CACD;GACD,mBAAmB,iBAAiB,wBAAwB;GAC5D,UAAU,iBAAiB,YAAY;GACvC,YACC,iBAAiB,YAAY,cAC7B,iBAAiB;GAClB,gBAAgB,iBAAiB,YAAY;GAC7C,cAAc,iBAAiB,mBAC5B,CAAC,iBAAiB,iBAAiB,GACnC;GACH,CAAC;EAGF,MAAM,UAAU,iBAAiB;EACjC,MAAM,MAAM,CAAC,SAAS,WACnB,KAAK,iBAAiB;GACtB,UAAU,SAAS,YAAY,iBAAiB;GAChD,qBAAqB,SAAS,uBAAuB,CACpD;IACC,SAAS;IACT,UAAU,iBAAiB;IAC3B,CACD;GACD,aAAa,SAAS,QAAQ,iBAAiB;GAC/C,CAAC,GACD,KAAK,iBAAiB,EACtB,UAAU,QAAQ,UAClB,CAAC;AAEJ,MAAI;AACH,2BAAwB,aAAa;WAC7B,OAAO;AACf,OAAI,iBAAiB,UAAU;IAC9B,MAAM,cACL,cAAc,iBAAiB,eAAe,IAAI,QAAQ;IAC3D,MAAM,YACL,MAAM,MAAM,SAAS,6BAClB,wBACA;AACJ,UAAM,IAAI,SACT,GAAG,YAAY,SAAS,UAAU,qBAAqB,mBAAmB,MAAM,QAAQ,GACxF;;AAEF,SAAM;;EAIP,IAAI;AACJ,MAAI;AACH,oBAAiB,MAAM,GAAG,mBAAmB,KAAK,QAAQ,EACzD,MAAM;IACL;IACA,YAAY,cAAc;IAC1B,EACD,CAAC;AAEF,OAAI,CAAC,gBAAgB,QACpB,OAAM,IAAI,MAAM,kCAAkC;WAE3C,OAAO;AACf,OAAI,QAAQ,OAAO,MAAM,mCAAmC;IAC3D;IACA,iBAAiB,OAAO,KAAK,cAAc,SAAS,CAAC,SACpD,QACA;IACD,CAAC;AACF,SAAM,IAAI,SAAS,eAAe;IACjC,SAAS;IACT,SAAS,iBAAiB,QAAQ,MAAM,UAAU,OAAO,MAAM;IAC/D,CAAC;;EAGH,MAAM,EAAE,YAAY;AAEpB,yBAAuB,gBAAgB,SAAS,MAAM,WAAW;AAEjE,wBAAuB,QAAgB,YAAY;GAClD,WAAW,SAAS,MAAM;GAC1B,mBAAmB,SAAS,MAAM;GAClC,QAAQ,IAAI,QAAQ;GACpB,CAAC;EAEF,MAAM,kBAAmB,QAAgB;AAMzC,MAFC,SAAS,MAAM,8BAEmB;GAClC,MAAM,oBAAoB,SAAS,MAAM,sBAAsB;AAE/D,OAAI,iBAAiB;IACpB,IAAI,gBAA2C;IAE/C,MAAM,eACL,MAAM,IAAI,QAAQ,gBAAgB,sBACjC,GAAG,2BAA2B,kBAC9B;AACF,QAAI,aACH,KAAI;AACH,qBAAgB,KAAK,MACpB,aAAa,MACb;AACD,SAAI,iBAAiB,cAAc,YAAY,KAAK,KAAK,CACxD,iBAAgB;YAEV;AACP,qBAAgB;;AAIlB,QAAI,CAAC,eAAe;AACnB,SAAI,QAAQ,OAAO,MAClB,sEACA;MAAE,cAAc;MAAiB;MAAY,CAC7C;KACD,MAAM,cACL,cAAc,iBAAiB,eAAe,IAAI,QAAQ;AAC3D,WAAM,IAAI,SACT,GAAG,YAAY,8EACf;;AAGF,QAAI,cAAc,eAAe,YAAY;AAC5C,SAAI,QAAQ,OAAO,MAClB,0DACA;MACC,cAAc;MACd,kBAAkB,cAAc;MAChC,gBAAgB;MAChB,CACD;AACD,WAAM,IAAI,QAAQ,gBAAgB,+BACjC,GAAG,2BAA2B,kBAC9B;KACD,MAAM,cACL,cAAc,iBAAiB,eAAe,IAAI,QAAQ;AAC3D,WAAM,IAAI,SACT,GAAG,YAAY,kEACf;;AAGF,UAAM,IAAI,QAAQ,gBAAgB,+BACjC,GAAG,2BAA2B,kBAC9B;cACS,CAAC,mBAAmB;AAC9B,QAAI,QAAQ,OAAO,MAClB,wFACA,EAAE,YAAY,CACd;IACD,MAAM,cACL,cAAc,iBAAiB,eAAe,IAAI,QAAQ;AAC3D,UAAM,IAAI,SACT,GAAG,YAAY,6EACf;;;EAQH,MAAM,iBAAiB,mBAHA,OAAO,KAAK,cAAc,SAAS,CAAC,SAC1D,QACA,CACwD;AAEzD,MAAI,gBAAgB;GACnB,MAAM,SAAS,IAAI,WAAW,aAAa;GAC3C,MAAM,aAAc,QAAgB;GAGpC,MAAM,YAAY,SAAS,MAAM,aAAa;GAC9C,MAAM,YAAY,YAAY,eAC3B,IAAI,KAAK,WAAW,aAAa,CAAC,SAAS,GAAG,YAC9C,KAAK,KAAK,GAAG;GAEhB,MAAM,oBACL,MAAM,IAAI,QAAQ,gBAAgB,sBACjC,GAAG,4BAA4B,iBAC/B;GAEF,IAAI,WAAW;AACf,OAAI,kBACH,KAAI;AAEH,QADe,KAAK,MAAM,kBAAkB,MAAM,CACvC,aAAa,KAAK,KAAK,CACjC,YAAW;YAEJ,OAAO;AACf,QAAI,QAAQ,OAAO,KAAK,2CAA2C;KAClE,aAAa;KACb;KACA,CAAC;;AAIJ,OAAI,UAAU;AACb,QAAI,QAAQ,OAAO,MAClB,6DACA;KACC,aAAa;KACb;KACA;KACA,CACD;IACD,MAAM,cACL,cAAc,iBAAiB,eAAe,IAAI,QAAQ;AAC3D,UAAM,IAAI,SACT,GAAG,YAAY,+EACf;;AAGF,SAAM,IAAI,QAAQ,gBAAgB,wBAAwB;IACzD,YAAY,GAAG,4BAA4B;IAC3C,OAAO,KAAK,UAAU;KACrB,aAAa;KACb;KACA;KACA,QAAQ,KAAK,KAAK;KAClB;KACA,CAAC;IACF,WAAW,IAAI,KAAK,UAAU;IAC9B,CAAC;QAEF,KAAI,QAAQ,OAAO,KAClB,wDACA,EAAE,YAAY,CACd;EAGF,MAAM,aAAa,QAAQ,cAAc,EAAE;EAC3C,MAAM,UAAU,iBAAiB,WAAW,EAAE;EAE9C,MAAM,WAAW;GAChB,GAAG,OAAO,YACT,OAAO,QAAQ,QAAQ,eAAe,EAAE,CAAC,CAAC,KAAK,CAAC,KAAK,WAAW,CAC/D,KACA,WAAW,OACX,CAAC,CACF;GACD,IAAI,WAAW,QAAQ,MAAM,aAAa,QAAQ;GAClD,QACC,WAAW,QAAQ,SAAS,YAAY,QAAQ,QAC/C,aAAa;GACf,MACC,CACC,WAAW,QAAQ,aAAa,cAChC,WAAW,QAAQ,YAAY,WAC/B,CACC,OAAO,QAAQ,CACf,KAAK,IAAI,IACX,WAAW,QAAQ,QAAQ,kBAC3B,QAAQ;GACT,eACC,SAAS,sBAAsB,QAAQ,gBAClC,WAAW,QAAQ,kBAAkB,QACvC;GACJ;AAED,MAAI,CAAC,SAAS,MAAM,CAAC,SAAS,OAAO;AACpC,OAAI,QAAQ,OAAO,MAClB,kDACA;IACC,YAAY,OAAO,KAAK,WAAW;IACnC;IACA,aAAa,SAAS;IACtB,gBAAgB,SAAS;IACzB,CACD;AACD,SAAM,IAAI,SAAS,eAAe,EACjC,SAAS,yDACT,CAAC;;EAGH,MAAM,oBACL,CAAC,CAAC,IAAI,QAAQ,QAAQ,SAAS,gBAAgB,kBAAkB,SAChE,SAAS,WACT,IACA,oBAAoB,YACpB,CAAC,CAAE,SAA0C,kBAC7C,oBAAoB,SAAS,OAAiB,SAAS,OAAO;EAEhE,MAAM,cACL,cAAc,iBAAiB,eAAe,IAAI,QAAQ;EAE3D,MAAM,SAAS,MAAM,oBAAoB,KAAK;GAC7C,UAAU;IACT,OAAO,SAAS;IAChB,MAAO,SAAS,QAAQ,SAAS;IACjC,IAAI,SAAS;IACb,eAAe,QAAQ,SAAS,cAAc;IAC9C;GACD,SAAS;IACR,YAAY,SAAS;IACrB,WAAW,SAAS;IACpB,aAAa;IACb,cAAc;IACd;GACD,aAAa;GACb,eAAe,SAAS;GACxB;GACA,CAAC;AAEF,MAAI,OAAO,MACV,OAAM,IAAI,SACT,GAAG,YAAY,SAAS,OAAO,MAAM,MAAM,IAAI,CAAC,KAAK,IAAI,GACzD;EAGF,MAAM,EAAE,SAAS,SAAS,OAAO;AAEjC,MAAI,SAAS,cACZ,OAAM,QAAQ,cAAc;GACrB;GACN;GACA;GACA,CAAC;AAGH,QAAM,+BAA+B,KAAY;GAChD;GACA,SAAS;IACR,cAAc;IACd,YAAY,SAAS;IACrB,WAAW,SAAS;IACpB,OAAO,SAAS;IAChB,eAAe,QAAQ,SAAS,cAAc;IAC9C,eAAe;IACf;GACD;GACA,qBAAqB,SAAS;GAC9B,CAAC;AAEF,QAAM,iBAAiB,KAAK;GAAE;GAAS;GAAM,CAAC;AAC9C,QAAM,IAAI,SAAS,YAAY;GAEhC;;;;;ACtmFF,KAAK,mBAViB,EACrB,MAAM,SAAS,KAAa;AAI3B,KAHgB,aAAa,SAAS,KAAK,EAC1C,wBAAwB,MACxB,CAAC,KACc,KAAM,QAAO;AAC7B,OAAM;GAEP,CAEqC;;;;;;AAiCtC,MAAM,+BAA+B,CACpC,uBACA,oBACA;AAqBD,SAAgB,IACf,SACmB;CACnB,MAAM,mBAAmB;CAEzB,IAAI,YAAY;EACf,YAAY,YAAY;EACxB,qBAAqB,oBAAoB,iBAAiB;EAC1D,WAAW,UAAU,iBAAiB;EACtC,aAAa,YAAY,iBAAiB;EAC1C,iBAAiB,gBAAgB,iBAAiB;EAClD,aAAa,YAAY,iBAAiB;EAC1C,kBAAkB,kBAAkB;EACpC,gBAAgB,gBAAgB;EAChC,mBAAmB,kBAAkB,iBAAiB;EACtD,mBAAmB,mBAAmB;EACtC;AAED,KAAI,SAAS,oBAAoB,SAAS;EACzC,MAAM,8BAA8B;GACnC,2BAA2B,0BAA0B,iBAAiB;GACtE,cAAc,aAAa,iBAAiB;GAC5C;AAED,cAAY;GACX,GAAG;GACH,GAAG;GACH;;AAGF,QAAO;EACN,IAAI;EACJ,KAAK,KAAK;GACT,MAAM,WAAW,IAAI;AACrB,OAAI,aAAa,KAChB,QAAO,EAAE;AAGV,UAAO,EACN,SAAS,EACR,iBAAiB,CAAC,GAHE,MAAM,QAAQ,SAAS,GAAG,WAAW,EAAE,EAGvB,GAAG,6BAA6B,EACpE,EACD;;EAEF;EACA,OAAO,EACN,OAAO,CACN;GACC,QAAQ,SAAS;AAChB,WAAO,QAAQ,MAAM,WAAW,aAAa,IAAI;;GAElD,SAAS,qBAAqB,OAAO,QAAQ;IAC5C,MAAM,aAAa,IAAI,QAAQ;AAC/B,QAAI,CAAC,YAAY,KAChB;AAGD,QAAI,CAAC,IAAI,QAAQ,UAAU,eAAe,CACzC;AAGD,UAAM,2BAA2B,KAAK;KACrC,MAAM,WAAW;KACjB,qBAAqB,SAAS;KAC9B,oBAAoB,SAAS;KAC7B,CAAC;KACD;GACF,CACD,EACD;EACD,QAAQ,EACP,aAAa;GACZ,WAAW,SAAS,aAAa;GACjC,QAAQ;IACP,QAAQ;KACP,MAAM;KACN,UAAU;KACV,WAAW,SAAS,QAAQ,UAAU;KACtC;IACD,YAAY;KACX,MAAM;KACN,UAAU;KACV,WAAW,SAAS,QAAQ,cAAc;KAC1C;IACD,YAAY;KACX,MAAM;KACN,UAAU;KACV,WAAW,SAAS,QAAQ,cAAc;KAC1C;IACD,QAAQ;KACP,MAAM;KACN,YAAY;MACX,OAAO;MACP,OAAO;MACP;KACD,WAAW,SAAS,QAAQ,UAAU;KACtC;IACD,YAAY;KACX,MAAM;KACN,UAAU;KACV,QAAQ;KACR,WAAW,SAAS,QAAQ,cAAc;KAC1C;IACD,gBAAgB;KACf,MAAM;KACN,UAAU;KACV,WAAW,SAAS,QAAQ,kBAAkB;KAC9C;IACD,QAAQ;KACP,MAAM;KACN,UAAU;KACV,WAAW,SAAS,QAAQ,UAAU;KACtC;IACD,GAAI,SAAS,oBAAoB,UAC9B,EAAE,gBAAgB;KAAE,MAAM;KAAW,UAAU;KAAO,EAAE,GACxD,EAAE;IACL;GACD,EACD;EACQ;EACT"}
|