UNPKG

@credo-ts/core

Version:

<p align="center"> <br /> <img alt="Credo Logo" src="https://github.com/openwallet-foundation/credo-ts/blob/c7886cb8377ceb8ee4efe8d264211e561a75072d/images/credo-logo.png" height="250px" /> </p> <h1 align="center"><b>Credo - Core</b></h1

1 lines • 21.4 kB
{"version":3,"file":"credentialSelection.mjs","names":[],"sources":["../../../../src/modules/dif-presentation-exchange/utils/credentialSelection.ts"],"sourcesContent":["import {\n type IPresentationDefinition,\n type PEX,\n type SelectResults,\n Status,\n type SubmissionRequirementMatch,\n} from '@animo-id/pex'\nimport type {\n SubmissionRequirementMatchFrom,\n SubmissionRequirementMatchInputDescriptor,\n} from '@animo-id/pex/dist/main/lib/evaluation/core'\nimport { SubmissionRequirementMatchType } from '@animo-id/pex/dist/main/lib/evaluation/core/index.js'\nimport { JSONPath } from '@astronautlabs/jsonpath'\nimport { decodeSdJwtSync, getClaimsSync } from '@sd-jwt/core'\nimport type { InputDescriptorV1, InputDescriptorV2, SubmissionRequirement } from '@sphereon/pex-models'\nimport { Rules } from '@sphereon/pex-models'\nimport type { AgentContext } from '../../../agent'\nimport { Hasher } from '../../../crypto'\nimport { CredoError } from '../../../error'\nimport type { JsonObject } from '../../../types'\nimport { MdocRecord } from '../../mdoc'\nimport { MdocDeviceResponse } from '../../mdoc/MdocDeviceResponse'\nimport { SdJwtVcRecord } from '../../sd-jwt-vc'\nimport { ClaimFormat, W3cCredentialRecord } from '../../vc'\nimport { DifPresentationExchangeError } from '../DifPresentationExchangeError'\nimport type {\n DifPexCredentialsForRequest,\n DifPexCredentialsForRequestRequirement,\n DifPexCredentialsForRequestSubmissionEntry,\n SubmissionEntryCredential,\n} from '../models'\nimport { getSphereonOriginalVerifiableCredential } from './transform'\n\nexport async function getCredentialsForRequest(\n agentContext: AgentContext,\n // PEX instance with hasher defined\n pex: PEX,\n presentationDefinition: IPresentationDefinition,\n credentialRecords: Array<W3cCredentialRecord | SdJwtVcRecord | MdocRecord>\n): Promise<DifPexCredentialsForRequest> {\n const encodedCredentials = credentialRecords.map(getSphereonOriginalVerifiableCredential)\n const selectResultsRaw = pex.selectFrom(presentationDefinition, encodedCredentials)\n\n const selectResults: CredentialRecordSelectResults = {\n ...selectResultsRaw,\n matches: selectResultsRaw.matches ?? [],\n // Map the encoded credential to their respective credential record\n verifiableCredential:\n selectResultsRaw.verifiableCredential?.map((selectedEncoded, index): SubmissionEntryCredential => {\n const credentialRecordIndex = selectResultsRaw.vcIndexes?.[index]\n if (credentialRecordIndex === undefined || credentialRecordIndex === -1) {\n throw new DifPresentationExchangeError('Unable to find credential in credential records.')\n }\n const credentialRecord = credentialRecords[credentialRecordIndex]\n if (credentialRecord instanceof SdJwtVcRecord) {\n // selectedEncoded always string when SdJwtVcRecord\n // Get the decoded payload from the the selected credential, this already has SD applied\n const { jwt, disclosures } = decodeSdJwtSync(selectedEncoded as string, Hasher.hash)\n const prettyClaims = getClaimsSync(jwt.payload, disclosures, Hasher.hash)\n\n return {\n claimFormat: ClaimFormat.SdJwtDc,\n credentialRecord,\n disclosedPayload: prettyClaims as JsonObject,\n }\n }\n if (credentialRecord instanceof MdocRecord) {\n return {\n claimFormat: ClaimFormat.MsoMdoc,\n credentialRecord,\n disclosedPayload: {},\n }\n }\n if (credentialRecord instanceof W3cCredentialRecord) {\n return {\n claimFormat: credentialRecord.firstCredential.claimFormat,\n credentialRecord,\n }\n }\n throw new CredoError('Unrecognized credential record type')\n }) ?? [],\n }\n\n const presentationSubmission: DifPexCredentialsForRequest = {\n requirements: [],\n areRequirementsSatisfied: false,\n name: presentationDefinition.name,\n purpose: presentationDefinition.purpose,\n }\n\n // If there's no submission requirements, ALL input descriptors MUST be satisfied\n if (!presentationDefinition.submission_requirements || presentationDefinition.submission_requirements.length === 0) {\n presentationSubmission.requirements = getSubmissionRequirementsForAllInputDescriptors(\n presentationDefinition.input_descriptors,\n selectResults\n )\n } else {\n presentationSubmission.requirements = getSubmissionRequirements(presentationDefinition, selectResults)\n }\n\n const allEntries = presentationSubmission.requirements.flatMap((requirement) => requirement.submissionEntry)\n\n const inputDescriptorsForMdocCredential = new Map<SubmissionEntryCredential, Set<string>>()\n for (const entry of allEntries)\n for (const verifiableCredential of entry.verifiableCredentials) {\n if (verifiableCredential.claimFormat !== ClaimFormat.MsoMdoc) continue\n\n const set = inputDescriptorsForMdocCredential.get(verifiableCredential) ?? new Set()\n set.add(entry.inputDescriptorId)\n inputDescriptorsForMdocCredential.set(verifiableCredential, set)\n }\n\n // NOTE: it might be better to apply disclosure per credential/match (as that's also how mdoc does this)\n // however this doesn't work very well in wallets, as you usually won't show the same credential twice with\n // different disclosed attributes\n // Apply limit disclosure for all mdocs\n for (const [verifiableCredential, inputDescriptorIds] of inputDescriptorsForMdocCredential.entries()) {\n if (verifiableCredential.claimFormat !== ClaimFormat.MsoMdoc) continue\n\n const inputDescriptorsForCredential = presentationDefinition.input_descriptors.filter(({ id }) =>\n inputDescriptorIds.has(id)\n )\n\n const mdoc = verifiableCredential.credentialRecord.firstCredential\n verifiableCredential.disclosedPayload = await MdocDeviceResponse.limitDisclosureToInputDescriptor(agentContext, {\n inputDescriptor: {\n id: mdoc.docType,\n format: {\n mso_mdoc: {\n alg: [],\n },\n },\n constraints: {\n limit_disclosure: 'required',\n fields: inputDescriptorsForCredential.flatMap((i) => i.constraints?.fields ?? []),\n },\n },\n mdoc,\n })\n }\n\n // There may be no requirements if we filter out all optional ones. To not makes things too complicated, we see it as an error\n // for now if a request is made that has no required requirements (but only e.g. min: 0, which means we don't need to disclose anything)\n // I see this more as the fault of the presentation definition, as it should have at least some requirements.\n if (presentationSubmission.requirements.length === 0) {\n throw new DifPresentationExchangeError(\n 'Presentation Definition does not require any credentials. Optional credentials are not included in the presentation submission.'\n )\n }\n\n if (selectResults.areRequiredCredentialsPresent === Status.ERROR) {\n return presentationSubmission\n }\n\n return {\n ...presentationSubmission,\n\n // If all requirements are satisfied, the presentation submission is satisfied\n areRequirementsSatisfied: presentationSubmission.requirements.every(\n (requirement) => requirement.isRequirementSatisfied\n ),\n }\n}\n\nfunction getSubmissionRequirements(\n presentationDefinition: IPresentationDefinition,\n selectResults: CredentialRecordSelectResults\n): Array<DifPexCredentialsForRequestRequirement> {\n const submissionRequirements: Array<DifPexCredentialsForRequestRequirement> = []\n\n const matches = selectResults.matches as SubmissionRequirementMatchFrom[]\n if (!matches.every((match) => match.type === SubmissionRequirementMatchType.SubmissionRequirement && match.from)) {\n throw new DifPresentationExchangeError(\n `Expected all matches to be of type '${SubmissionRequirementMatchType.SubmissionRequirement}' with 'from' key.`\n )\n }\n\n // There are submission requirements, so we need to select the input_descriptors\n // based on the submission requirements\n presentationDefinition.submission_requirements?.forEach((submissionRequirement, submissionRequirementIndex) => {\n // Check: if the submissionRequirement uses `from_nested`, as we don't support this yet\n if (submissionRequirement.from_nested) {\n throw new DifPresentationExchangeError(\n \"Presentation definition contains requirement using 'from_nested', which is not supported yet.\"\n )\n }\n\n // Check if there's a 'from'. If not the structure is not as we expect it\n if (!submissionRequirement.from) {\n throw new DifPresentationExchangeError(\"Missing 'from' in submission requirement match\")\n }\n\n const match = matches.find((match) => match.id === submissionRequirementIndex)\n if (!match) {\n throw new Error(`Unable to find a match for submission requirement with index '${submissionRequirementIndex}'`)\n }\n\n if (submissionRequirement.rule === Rules.All) {\n const selectedSubmission = getSubmissionRequirementRuleAll(\n submissionRequirement,\n presentationDefinition,\n selectResults.verifiableCredential,\n match\n )\n submissionRequirements.push(selectedSubmission)\n } else {\n const selectedSubmission = getSubmissionRequirementRulePick(\n submissionRequirement,\n presentationDefinition,\n selectResults.verifiableCredential,\n match\n )\n\n submissionRequirements.push(selectedSubmission)\n }\n })\n\n // Submission may have requirement that doesn't require a credential to be submitted (e.g. min: 0)\n // We use minimization strategy, and thus only disclose the minimum amount of information\n const requirementsWithCredentials = submissionRequirements.filter((requirement) => requirement.needsCount > 0)\n\n return requirementsWithCredentials\n}\n\nfunction getSubmissionRequirementsForAllInputDescriptors(\n inputDescriptors: Array<InputDescriptorV1> | Array<InputDescriptorV2>,\n selectResults: CredentialRecordSelectResults\n): Array<DifPexCredentialsForRequestRequirement> {\n const submissionRequirements: Array<DifPexCredentialsForRequestRequirement> = []\n\n const matches = selectResults.matches as SubmissionRequirementMatchInputDescriptor[]\n if (!matches.every((match) => match.type === SubmissionRequirementMatchType.InputDescriptor)) {\n throw new DifPresentationExchangeError(\n `Expected all matches to be of type '${SubmissionRequirementMatchType.InputDescriptor}' when.`\n )\n }\n\n for (const inputDescriptor of inputDescriptors) {\n const submission = getSubmissionForInputDescriptor(inputDescriptor, selectResults.verifiableCredential, matches)\n submissionRequirements.push({\n rule: Rules.Pick,\n needsCount: 1, // Every input descriptor is a distinct requirement, so the count is always 1,\n submissionEntry: [submission],\n isRequirementSatisfied: submission.verifiableCredentials.length >= 1,\n })\n }\n\n return submissionRequirements\n}\n\nfunction getSubmissionRequirementRuleAll(\n submissionRequirement: SubmissionRequirement,\n presentationDefinition: IPresentationDefinition,\n verifiableCredentials: SubmissionEntryCredential[],\n match: SubmissionRequirementMatchFrom\n) {\n // Check if there's a 'from'. If not the structure is not as we expect it\n if (!submissionRequirement.from)\n throw new DifPresentationExchangeError(\"Missing 'from' in submission requirement match.\")\n\n const selectedSubmission: DifPexCredentialsForRequestRequirement = {\n rule: Rules.All,\n needsCount: 0,\n name: submissionRequirement.name,\n purpose: submissionRequirement.purpose,\n submissionEntry: [],\n isRequirementSatisfied: false,\n }\n\n for (const inputDescriptor of presentationDefinition.input_descriptors) {\n // We only want to get the submission if the input descriptor belongs to the group\n if (!inputDescriptor.group?.includes(match.from)) continue\n\n const submission = getSubmissionForInputDescriptor(inputDescriptor, verifiableCredentials, match.input_descriptors)\n\n // Rule ALL, so for every input descriptor that matches in this group, we need to add it\n selectedSubmission.needsCount += 1\n selectedSubmission.submissionEntry.push(submission)\n }\n\n return {\n ...selectedSubmission,\n\n // If all submissions have a credential, the requirement is satisfied\n isRequirementSatisfied: selectedSubmission.submissionEntry.every(\n (submission) => submission.verifiableCredentials.length >= 1\n ),\n }\n}\n\nfunction getSubmissionRequirementRulePick(\n submissionRequirement: SubmissionRequirement,\n presentationDefinition: IPresentationDefinition,\n verifiableCredentials: SubmissionEntryCredential[],\n match: SubmissionRequirementMatchFrom\n) {\n // Check if there's a 'from'. If not the structure is not as we expect it\n if (!submissionRequirement.from) {\n throw new DifPresentationExchangeError(\"Missing 'from' in submission requirement match.\")\n }\n\n const selectedSubmission: DifPexCredentialsForRequestRequirement = {\n rule: Rules.Pick,\n needsCount: submissionRequirement.count ?? submissionRequirement.min ?? 1,\n name: submissionRequirement.name,\n purpose: submissionRequirement.purpose,\n // If there's no count, min, or max we assume one credential is required for submission\n // however, the exact behavior is not specified in the spec\n submissionEntry: [],\n isRequirementSatisfied: false,\n }\n\n const satisfiedSubmissions: Array<DifPexCredentialsForRequestSubmissionEntry> = []\n const unsatisfiedSubmissions: Array<DifPexCredentialsForRequestSubmissionEntry> = []\n\n for (const inputDescriptor of presentationDefinition.input_descriptors) {\n // We only want to get the submission if the input descriptor belongs to the group\n if (!inputDescriptor.group?.includes(match.from)) continue\n\n const submission = getSubmissionForInputDescriptor(inputDescriptor, verifiableCredentials, match.input_descriptors)\n\n if (submission.verifiableCredentials.length >= 1) {\n satisfiedSubmissions.push(submission)\n } else {\n unsatisfiedSubmissions.push(submission)\n }\n\n // If we have found enough credentials to satisfy the requirement, we could stop\n // but the user may not want the first x that match, so we continue and return all matches\n // if (satisfiedSubmissions.length === selectedSubmission.needsCount) break\n }\n\n return {\n ...selectedSubmission,\n\n // If there are enough satisfied submissions, the requirement is satisfied\n isRequirementSatisfied: satisfiedSubmissions.length >= selectedSubmission.needsCount,\n\n // if the requirement is satisfied, we only need to return the satisfied submissions\n // however if the requirement is not satisfied, we include all entries so the wallet could\n // render which credentials are missing.\n submissionEntry:\n satisfiedSubmissions.length >= selectedSubmission.needsCount\n ? satisfiedSubmissions\n : [...satisfiedSubmissions, ...unsatisfiedSubmissions],\n }\n}\n\nfunction getSubmissionForInputDescriptor(\n inputDescriptor: InputDescriptorV1 | InputDescriptorV2,\n verifiableCredentials: SubmissionEntryCredential[],\n matches: SubmissionRequirementMatchInputDescriptor[]\n): DifPexCredentialsForRequestSubmissionEntry {\n const matchesForInputDescriptor = matches.filter((m) => m.id === inputDescriptor.id)\n\n const submissionEntry: DifPexCredentialsForRequestSubmissionEntry = {\n inputDescriptorId: inputDescriptor.id,\n name: inputDescriptor.name,\n purpose: inputDescriptor.purpose,\n verifiableCredentials: matchesForInputDescriptor.flatMap((matchForInputDescriptor) =>\n extractCredentialsFromInputDescriptorMatch(matchForInputDescriptor, verifiableCredentials)\n ),\n }\n\n // return early if no matches.\n if (!matchesForInputDescriptor?.length) return submissionEntry\n\n return submissionEntry\n}\n\nfunction extractCredentialsFromInputDescriptorMatch(\n match: SubmissionRequirementMatchInputDescriptor,\n availableCredentials: SubmissionEntryCredential[]\n) {\n const verifiableCredentials: SubmissionEntryCredential[] = []\n\n for (const vcPath of match.vc_path) {\n const [verifiableCredential] = JSONPath.query(\n { verifiableCredential: availableCredentials },\n vcPath\n ) as SubmissionEntryCredential[]\n verifiableCredentials.push(verifiableCredential)\n }\n\n return verifiableCredentials\n}\n\n/**\n * Custom SelectResults that includes the Credo records instead of the encoded verifiable credential\n */\ntype CredentialRecordSelectResults = Omit<SelectResults, 'verifiableCredential'> & {\n verifiableCredential: SubmissionEntryCredential[]\n matches: SubmissionRequirementMatch[]\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAiCA,eAAsB,yBACpB,cAEA,KACA,wBACA,mBACsC;CACtC,MAAM,qBAAqB,kBAAkB,IAAI,uCAAuC;CACxF,MAAM,mBAAmB,IAAI,WAAW,wBAAwB,kBAAkB;CAElF,MAAM,gBAA+C;EACnD,GAAG;EACH,SAAS,iBAAiB,WAAW,CAAC;EAEtC,sBACE,iBAAiB,sBAAsB,KAAK,iBAAiB,UAAqC;GAChG,MAAM,wBAAwB,iBAAiB,YAAY;GAC3D,IAAI,0BAA0B,KAAA,KAAa,0BAA0B,IACnE,MAAM,IAAI,6BAA6B,kDAAkD;GAE3F,MAAM,mBAAmB,kBAAkB;GAC3C,IAAI,4BAA4B,eAAe;IAG7C,MAAM,EAAE,KAAK,gBAAgB,gBAAgB,iBAA2B,OAAO,IAAI;IAGnF,OAAO;KACL,aAAA;KACA;KACA,kBALmB,cAAc,IAAI,SAAS,aAAa,OAAO,IAKhD;IACpB;GACF;GACA,IAAI,4BAA4B,YAC9B,OAAO;IACL,aAAA;IACA;IACA,kBAAkB,CAAC;GACrB;GAEF,IAAI,4BAA4B,qBAC9B,OAAO;IACL,aAAa,iBAAiB,gBAAgB;IAC9C;GACF;GAEF,MAAM,IAAI,WAAW,qCAAqC;EAC5D,CAAC,KAAK,CAAC;CACX;CAEA,MAAM,yBAAsD;EAC1D,cAAc,CAAC;EACf,0BAA0B;EAC1B,MAAM,uBAAuB;EAC7B,SAAS,uBAAuB;CAClC;CAGA,IAAI,CAAC,uBAAuB,2BAA2B,uBAAuB,wBAAwB,WAAW,GAC/G,uBAAuB,eAAe,gDACpC,uBAAuB,mBACvB,aACF;MAEA,uBAAuB,eAAe,0BAA0B,wBAAwB,aAAa;CAGvG,MAAM,aAAa,uBAAuB,aAAa,SAAS,gBAAgB,YAAY,eAAe;CAE3G,MAAM,oDAAoC,IAAI,IAA4C;CAC1F,KAAK,MAAM,SAAS,YAClB,KAAK,MAAM,wBAAwB,MAAM,uBAAuB;EAC9D,IAAI,qBAAqB,gBAAA,YAAqC;EAE9D,MAAM,MAAM,kCAAkC,IAAI,oBAAoB,qBAAK,IAAI,IAAI;EACnF,IAAI,IAAI,MAAM,iBAAiB;EAC/B,kCAAkC,IAAI,sBAAsB,GAAG;CACjE;CAMF,KAAK,MAAM,CAAC,sBAAsB,uBAAuB,kCAAkC,QAAQ,GAAG;EACpG,IAAI,qBAAqB,gBAAA,YAAqC;EAE9D,MAAM,gCAAgC,uBAAuB,kBAAkB,QAAQ,EAAE,SACvF,mBAAmB,IAAI,EAAE,CAC3B;EAEA,MAAM,OAAO,qBAAqB,iBAAiB;EACnD,qBAAqB,mBAAmB,MAAM,mBAAmB,iCAAiC,cAAc;GAC9G,iBAAiB;IACf,IAAI,KAAK;IACT,QAAQ,EACN,UAAU,EACR,KAAK,CAAC,EACR,EACF;IACA,aAAa;KACX,kBAAkB;KAClB,QAAQ,8BAA8B,SAAS,MAAM,EAAE,aAAa,UAAU,CAAC,CAAC;IAClF;GACF;GACA;EACF,CAAC;CACH;CAKA,IAAI,uBAAuB,aAAa,WAAW,GACjD,MAAM,IAAI,6BACR,iIACF;CAGF,IAAI,cAAc,kCAAkC,OAAO,OACzD,OAAO;CAGT,OAAO;EACL,GAAG;EAGH,0BAA0B,uBAAuB,aAAa,OAC3D,gBAAgB,YAAY,sBAC/B;CACF;AACF;AAEA,SAAS,0BACP,wBACA,eAC+C;CAC/C,MAAM,yBAAwE,CAAC;CAE/E,MAAM,UAAU,cAAc;CAC9B,IAAI,CAAC,QAAQ,OAAO,UAAU,MAAM,SAAS,+BAA+B,yBAAyB,MAAM,IAAI,GAC7G,MAAM,IAAI,6BACR,uCAAuC,+BAA+B,sBAAsB,mBAC9F;CAKF,uBAAuB,yBAAyB,SAAS,uBAAuB,+BAA+B;EAE7G,IAAI,sBAAsB,aACxB,MAAM,IAAI,6BACR,+FACF;EAIF,IAAI,CAAC,sBAAsB,MACzB,MAAM,IAAI,6BAA6B,gDAAgD;EAGzF,MAAM,QAAQ,QAAQ,MAAM,UAAU,MAAM,OAAO,0BAA0B;EAC7E,IAAI,CAAC,OACH,MAAM,IAAI,MAAM,iEAAiE,2BAA2B,EAAE;EAGhH,IAAI,sBAAsB,SAAS,MAAM,KAAK;GAC5C,MAAM,qBAAqB,gCACzB,uBACA,wBACA,cAAc,sBACd,KACF;GACA,uBAAuB,KAAK,kBAAkB;EAChD,OAAO;GACL,MAAM,qBAAqB,iCACzB,uBACA,wBACA,cAAc,sBACd,KACF;GAEA,uBAAuB,KAAK,kBAAkB;EAChD;CACF,CAAC;CAMD,OAFoC,uBAAuB,QAAQ,gBAAgB,YAAY,aAAa,CAE3E;AACnC;AAEA,SAAS,gDACP,kBACA,eAC+C;CAC/C,MAAM,yBAAwE,CAAC;CAE/E,MAAM,UAAU,cAAc;CAC9B,IAAI,CAAC,QAAQ,OAAO,UAAU,MAAM,SAAS,+BAA+B,eAAe,GACzF,MAAM,IAAI,6BACR,uCAAuC,+BAA+B,gBAAgB,QACxF;CAGF,KAAK,MAAM,mBAAmB,kBAAkB;EAC9C,MAAM,aAAa,gCAAgC,iBAAiB,cAAc,sBAAsB,OAAO;EAC/G,uBAAuB,KAAK;GAC1B,MAAM,MAAM;GACZ,YAAY;GACZ,iBAAiB,CAAC,UAAU;GAC5B,wBAAwB,WAAW,sBAAsB,UAAU;EACrE,CAAC;CACH;CAEA,OAAO;AACT;AAEA,SAAS,gCACP,uBACA,wBACA,uBACA,OACA;CAEA,IAAI,CAAC,sBAAsB,MACzB,MAAM,IAAI,6BAA6B,iDAAiD;CAE1F,MAAM,qBAA6D;EACjE,MAAM,MAAM;EACZ,YAAY;EACZ,MAAM,sBAAsB;EAC5B,SAAS,sBAAsB;EAC/B,iBAAiB,CAAC;EAClB,wBAAwB;CAC1B;CAEA,KAAK,MAAM,mBAAmB,uBAAuB,mBAAmB;EAEtE,IAAI,CAAC,gBAAgB,OAAO,SAAS,MAAM,IAAI,GAAG;EAElD,MAAM,aAAa,gCAAgC,iBAAiB,uBAAuB,MAAM,iBAAiB;EAGlH,mBAAmB,cAAc;EACjC,mBAAmB,gBAAgB,KAAK,UAAU;CACpD;CAEA,OAAO;EACL,GAAG;EAGH,wBAAwB,mBAAmB,gBAAgB,OACxD,eAAe,WAAW,sBAAsB,UAAU,CAC7D;CACF;AACF;AAEA,SAAS,iCACP,uBACA,wBACA,uBACA,OACA;CAEA,IAAI,CAAC,sBAAsB,MACzB,MAAM,IAAI,6BAA6B,iDAAiD;CAG1F,MAAM,qBAA6D;EACjE,MAAM,MAAM;EACZ,YAAY,sBAAsB,SAAS,sBAAsB,OAAO;EACxE,MAAM,sBAAsB;EAC5B,SAAS,sBAAsB;EAG/B,iBAAiB,CAAC;EAClB,wBAAwB;CAC1B;CAEA,MAAM,uBAA0E,CAAC;CACjF,MAAM,yBAA4E,CAAC;CAEnF,KAAK,MAAM,mBAAmB,uBAAuB,mBAAmB;EAEtE,IAAI,CAAC,gBAAgB,OAAO,SAAS,MAAM,IAAI,GAAG;EAElD,MAAM,aAAa,gCAAgC,iBAAiB,uBAAuB,MAAM,iBAAiB;EAElH,IAAI,WAAW,sBAAsB,UAAU,GAC7C,qBAAqB,KAAK,UAAU;OAEpC,uBAAuB,KAAK,UAAU;CAM1C;CAEA,OAAO;EACL,GAAG;EAGH,wBAAwB,qBAAqB,UAAU,mBAAmB;EAK1E,iBACE,qBAAqB,UAAU,mBAAmB,aAC9C,uBACA,CAAC,GAAG,sBAAsB,GAAG,sBAAsB;CAC3D;AACF;AAEA,SAAS,gCACP,iBACA,uBACA,SAC4C;CAC5C,MAAM,4BAA4B,QAAQ,QAAQ,MAAM,EAAE,OAAO,gBAAgB,EAAE;CAEnF,MAAM,kBAA8D;EAClE,mBAAmB,gBAAgB;EACnC,MAAM,gBAAgB;EACtB,SAAS,gBAAgB;EACzB,uBAAuB,0BAA0B,SAAS,4BACxD,2CAA2C,yBAAyB,qBAAqB,CAC3F;CACF;CAGA,IAAI,CAAC,2BAA2B,QAAQ,OAAO;CAE/C,OAAO;AACT;AAEA,SAAS,2CACP,OACA,sBACA;CACA,MAAM,wBAAqD,CAAC;CAE5D,KAAK,MAAM,UAAU,MAAM,SAAS;EAClC,MAAM,CAAC,wBAAwB,SAAS,MACtC,EAAE,sBAAsB,qBAAqB,GAC7C,MACF;EACA,sBAAsB,KAAK,oBAAoB;CACjD;CAEA,OAAO;AACT"}