@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 • 17.4 kB
Source Map (JSON)
{"version":3,"file":"PublicJwk.mjs","names":[],"sources":["../../../../src/modules/kms/jwk/PublicJwk.ts"],"sourcesContent":["import type { HashName } from '../../../crypto'\nimport { CredoError } from '../../../error'\nimport { MultiBaseEncoder, TypedArrayEncoder, VarintEncoder } from '../../../utils'\nimport type { Constructor } from '../../../utils/mixins'\nimport { zParseWithErrorHandling } from '../../../utils/zod'\nimport { KeyManagementError } from '../error/KeyManagementError'\nimport { legacyKeyIdFromPublicJwk } from '../legacy'\nimport { asymmetricPublicJwkMatches } from './equals'\nimport { getJwkHumanDescription } from './humanDescription'\nimport type { KnownJwaKeyAgreementAlgorithm, KnownJwaSignatureAlgorithm } from './jwa'\nimport { calculateJwkThumbprint } from './jwkThumbprint'\nimport { assertJwkAsymmetric, type KmsJwkPublicAsymmetric, publicJwkFromPrivateJwk, zKmsJwkPublic } from './knownJwk'\nimport {\n Ed25519PublicJwk,\n P256PublicJwk,\n P384PublicJwk,\n P521PublicJwk,\n RsaPublicJwk,\n Secp256k1PublicJwk,\n X25519PublicJwk,\n} from './kty'\n\nexport const SupportedPublicJwks = [\n Ed25519PublicJwk,\n P256PublicJwk,\n P384PublicJwk,\n P521PublicJwk,\n RsaPublicJwk,\n Secp256k1PublicJwk,\n X25519PublicJwk,\n]\nexport type SupportedPublicJwkClass = (typeof SupportedPublicJwks)[number]\nexport type SupportedPublicJwk =\n | Ed25519PublicJwk\n | P256PublicJwk\n | P384PublicJwk\n | P521PublicJwk\n | RsaPublicJwk\n | Secp256k1PublicJwk\n | X25519PublicJwk\n\ntype ExtractByJwk<T, K> = T extends { jwk: infer J } ? (K extends J ? T : never) : never\n\ntype ExtractByPublicKey<T, K> = T extends { publicKey: infer J } ? (K extends J ? T : never) : never\n\nexport class PublicJwk<Jwk extends SupportedPublicJwk = SupportedPublicJwk> {\n private constructor(private readonly jwk: Jwk) {}\n\n public static fromUnknown(jwkJson: unknown) {\n // We remove any private properties if they are present\n const publicJwk = publicJwkFromPrivateJwk(zParseWithErrorHandling(zKmsJwkPublic, jwkJson, 'jwk is not a valid jwk'))\n assertJwkAsymmetric(publicJwk)\n\n let jwkInstance: SupportedPublicJwk\n if (publicJwk.kty === 'RSA') {\n jwkInstance = new RsaPublicJwk(publicJwk)\n } else if (publicJwk.kty === 'EC') {\n if (publicJwk.crv === 'P-256') {\n jwkInstance = new P256PublicJwk({\n ...publicJwk,\n crv: publicJwk.crv,\n })\n } else if (publicJwk.crv === 'P-384') {\n jwkInstance = new P384PublicJwk({\n ...publicJwk,\n crv: publicJwk.crv,\n })\n } else if (publicJwk.crv === 'P-521') {\n jwkInstance = new P521PublicJwk({\n ...publicJwk,\n crv: publicJwk.crv,\n })\n } else if (publicJwk.crv === 'secp256k1') {\n jwkInstance = new Secp256k1PublicJwk({\n ...publicJwk,\n crv: publicJwk.crv,\n })\n } else {\n throw new KeyManagementError(\n `Unsupported kty '${publicJwk.kty}' with crv '${publicJwk.crv}' for creating jwk instance`\n )\n }\n } else if (publicJwk.crv === 'Ed25519') {\n jwkInstance = new Ed25519PublicJwk({\n ...publicJwk,\n crv: publicJwk.crv,\n })\n } else if (publicJwk.crv === 'X25519') {\n jwkInstance = new X25519PublicJwk({\n ...publicJwk,\n crv: publicJwk.crv,\n })\n } else {\n throw new KeyManagementError(`Unsupported kty '${publicJwk.kty}' for creating jwk instance`)\n }\n\n return new PublicJwk(jwkInstance)\n }\n\n // FIXME: all Jwk combinations should be separate types.\n // so not kty: EC, and crv: P-256 | P-384\n // but: kty: EC, and crv: P-256 | kty: EC, and crv: P-384\n // As the first approach messes with TypeScript's type inference\n public static fromPublicJwk<Jwk extends KmsJwkPublicAsymmetric>(jwk: Jwk) {\n return PublicJwk.fromUnknown(jwk) as PublicJwk<\n ExtractByJwk<SupportedPublicJwk, Jwk> extends never ? SupportedPublicJwk : ExtractByJwk<SupportedPublicJwk, Jwk>\n >\n }\n\n public toJson({ includeKid = true }: { includeKid?: boolean } = {}): Jwk['jwk'] {\n if (includeKid) return this.jwk.jwk\n\n const { kid, ...jwk } = this.jwk.jwk\n return jwk\n }\n\n /**\n * Get the signature algorithms supported for this jwk.\n *\n * If the jwk has an `alg` field defined it will only return that alg\n * and otherwise return all known supported signature algorithms.\n */\n public get supportedSignatureAlgorithms(): KnownJwaSignatureAlgorithm[] {\n const supportedSignatureAlgorithms: KnownJwaSignatureAlgorithm[] = this.jwk.supportedSignatureAlgorithms ?? []\n\n if (this.jwk.jwk.alg) {\n if (!supportedSignatureAlgorithms.includes(this.jwk.jwk.alg as KnownJwaSignatureAlgorithm)) {\n throw new KeyManagementError(\n `${this.jwkTypeHumanDescription} defines alg '${this.jwk.jwk.alg}' but this alg is not supported.`\n )\n }\n\n return [this.jwk.jwk.alg] as this['supportedSignatureAlgorithms']\n }\n\n return supportedSignatureAlgorithms\n }\n\n public get supportedEncryptionKeyAgreementAlgorithms(): KnownJwaKeyAgreementAlgorithm[] {\n return this.jwk.supportedEncryptionKeyAgreementAlgorithms ?? []\n }\n\n /**\n * key type as defined in [JWA Specification](https://tools.ietf.org/html/rfc7518#section-6.1)\n */\n public get kty(): Jwk['jwk']['kty'] {\n return this.jwk.jwk.kty\n }\n\n /**\n * Get the key id for a public jwk. If the public jwk does not have\n * a key id, an error will be thrown\n */\n public get keyId(): string {\n if (this.jwk.jwk.kid) return this.jwk.jwk.kid\n\n throw new KeyManagementError('Unable to determine keyId for jwk')\n }\n\n public get hasKeyId(): boolean {\n return this.jwk.jwk.kid !== undefined\n }\n\n public set keyId(keyId: string) {\n this.jwk.jwk.kid = keyId\n }\n\n public get legacyKeyId() {\n return legacyKeyIdFromPublicJwk(this)\n }\n\n public get publicKey(): Jwk['publicKey'] {\n return this.jwk.publicKey\n }\n\n /**\n * Return the compressed public key. If the key type does not support compressed public keys, it will return null\n */\n public get compressedPublicKey(): Jwk['compressedPublicKey'] {\n return this.jwk.compressedPublicKey\n }\n\n public get JwkClass() {\n return this.jwk.constructor as SupportedPublicJwkClass\n }\n\n /**\n * SHA-256 jwk thumbprint\n */\n public getJwkThumbprint(hashAlgorithm: HashName = 'sha-256') {\n return calculateJwkThumbprint({\n jwk: this.jwk.jwk,\n hashAlgorithm: hashAlgorithm,\n })\n }\n\n /**\n * Get the first signature algorithm to use with this jwk. If the jwk has an `alg` field defined\n * it will use that alg, and otherwise fall back to the first supported signature algorithm.\n *\n * If no algorithm is supported it will throw an error\n */\n public get signatureAlgorithm() {\n const alg = this.supportedSignatureAlgorithms[0]\n if (!alg) {\n throw new KeyManagementError(`${this.jwkTypeHumanDescription} has no supported signature algorithms`)\n }\n\n return alg as this['supportedSignatureAlgorithms'][number]\n }\n\n /**\n * The `alg` field of the jwk, indicating the algorithm the key is intended to be used with.\n */\n public get alg(): string | undefined {\n return this.jwk.jwk.alg\n }\n\n /**\n * Set the `alg` field of the jwk, indicating the algorithm the key is intended to be used\n * with. This restricts the jwk to that algorithm (e.g. {@link signatureAlgorithm} will\n * return the `alg` when defined).\n *\n * If the algorithm is not supported by this jwk an error will be thrown.\n */\n public set alg(alg: KnownJwaSignatureAlgorithm | KnownJwaKeyAgreementAlgorithm) {\n const supportedAlgorithms: Array<KnownJwaSignatureAlgorithm | KnownJwaKeyAgreementAlgorithm> = [\n ...(this.jwk.supportedSignatureAlgorithms ?? []),\n ...(this.jwk.supportedEncryptionKeyAgreementAlgorithms ?? []),\n ]\n\n if (!supportedAlgorithms.includes(alg)) {\n throw new KeyManagementError(`${this.jwkTypeHumanDescription} does not support alg '${alg}'.`)\n }\n\n this.jwk.jwk.alg = alg\n }\n\n public assertSignatureAlgorithmSupported(\n alg: KnownJwaSignatureAlgorithm\n ): asserts alg is this['supportedSignatureAlgorithms'][number] {\n if (!this.supportedSignatureAlgorithms.includes(alg)) {\n throw new KeyManagementError(`${this.jwkTypeHumanDescription} does not support signature alg '${alg}'.`)\n }\n }\n\n public static fromPublicKey<Supported extends SupportedPublicJwk['publicKey']>(publicKey: Supported) {\n let jwkInstance: SupportedPublicJwk\n\n if (publicKey.kty === 'RSA') {\n jwkInstance = RsaPublicJwk.fromPublicKey(publicKey)\n } else if (publicKey.kty === 'EC') {\n if (publicKey.crv === 'P-256') {\n jwkInstance = P256PublicJwk.fromPublicKey(publicKey.publicKey)\n } else if (publicKey.crv === 'P-384') {\n jwkInstance = P384PublicJwk.fromPublicKey(publicKey.publicKey)\n } else if (publicKey.crv === 'P-521') {\n jwkInstance = P521PublicJwk.fromPublicKey(publicKey.publicKey)\n } else if (publicKey.crv === 'secp256k1') {\n jwkInstance = Secp256k1PublicJwk.fromPublicKey(publicKey.publicKey)\n } else {\n throw new KeyManagementError(\n // @ts-expect-error\n `Unsupported kty '${publicKey.kty}' with crv '${publicKey.crv}' for creating jwk instance based on public key bytes`\n )\n }\n } else if (publicKey.crv === 'X25519') {\n jwkInstance = X25519PublicJwk.fromPublicKey(publicKey.publicKey)\n } else if (publicKey.crv === 'Ed25519') {\n jwkInstance = Ed25519PublicJwk.fromPublicKey(publicKey.publicKey)\n } else {\n throw new KeyManagementError(\n // @ts-expect-error\n `Unsupported kty '${publicKey.kty}' for creating jwk instance based on public key bytes`\n )\n }\n\n return new PublicJwk(jwkInstance) as PublicJwk<ExtractByPublicKey<SupportedPublicJwk, Supported>>\n }\n\n /**\n * Returns the jwk encoded a Base58 multibase encoded multicodec key\n */\n public get fingerprint() {\n const prefixBytes = VarintEncoder.encode(this.jwk.multicodecPrefix)\n const prefixedPublicKey = new Uint8Array([...prefixBytes, ...this.jwk.multicodec])\n\n return `z${TypedArrayEncoder.toBase58(prefixedPublicKey)}`\n }\n\n /**\n * Create a jwk instance based on a Base58 multibase encoded multicodec key\n */\n public static fromFingerprint(fingerprint: string) {\n const { data } = MultiBaseEncoder.decode(fingerprint)\n const [code, byteLength] = VarintEncoder.decode(data)\n const publicKey = data.slice(byteLength)\n\n const PublicJwkClass = SupportedPublicJwks.find((JwkClass) => JwkClass.multicodecPrefix === code)\n if (!PublicJwkClass) {\n throw new KeyManagementError(`Unsupported multicodec public key with prefix '${code}'`)\n }\n\n const jwk = PublicJwkClass.fromMulticodec(publicKey)\n return new PublicJwk(jwk)\n }\n\n /**\n * Check whether this PublicJwk instance is of a specific type\n */\n public is<\n Jwk1 extends SupportedPublicJwk,\n Jwk2 extends SupportedPublicJwk = Jwk1,\n Jwk3 extends SupportedPublicJwk = Jwk1,\n >(\n jwkType1: Constructor<Jwk1>,\n jwkType2?: Constructor<Jwk2>,\n jwkType3?: Constructor<Jwk3>\n ): this is PublicJwk<Jwk1> | PublicJwk<Jwk2> | PublicJwk<Jwk3> {\n const types = [jwkType1, jwkType2, jwkType3].filter(Boolean) as Constructor<SupportedPublicJwk>[]\n return types.some((type) => this.jwk.constructor === type)\n }\n\n /**\n * Convert the PublicJwk to another type.\n *\n * NOTE: only supported for Ed25519 to X25519 at the moment\n */\n public convertTo(\n type: Jwk extends Ed25519PublicJwk ? typeof X25519PublicJwk : never\n ): Jwk extends Ed25519PublicJwk ? PublicJwk<X25519PublicJwk> : never {\n if (!this.is(Ed25519PublicJwk) || type !== X25519PublicJwk) {\n throw new KeyManagementError('Unsupported key conversion. Only Ed25519 to X25519 is supported.')\n }\n\n return PublicJwk.fromPublicJwk(this.jwk.toX25519PublicJwk()) as Jwk extends Ed25519PublicJwk\n ? PublicJwk<X25519PublicJwk>\n : never\n }\n\n /**\n * Check whether this jwk instance is the same as another jwk instance.\n * It does this by comparing the key types and public keys, not other fields\n * of the JWK such as keyId, use, etc..\n */\n public equals(other: PublicJwk) {\n return asymmetricPublicJwkMatches(this.toJson(), other.toJson())\n }\n\n /**\n * Get human description of a jwk type. This does\n * not include the (public) key material\n */\n public get jwkTypeHumanDescription() {\n return getJwkHumanDescription(this.toJson())\n }\n\n public static supportedPublicJwkClassForSignatureAlgorithm(alg: KnownJwaSignatureAlgorithm): SupportedPublicJwkClass {\n const supportedPublicJwkClass = SupportedPublicJwks.find((JwkClass) =>\n JwkClass.supportedSignatureAlgorithms.includes(alg)\n )\n\n if (!supportedPublicJwkClass) {\n throw new CredoError(`Could not determine supported public jwk class for alg '${alg}'`)\n }\n\n return supportedPublicJwkClass\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;AAsBA,MAAa,sBAAsB;CACjC;CACA;CACA;CACA;CACA;CACA;CACA;AACF;AAeA,IAAa,YAAb,MAAa,UAA+D;CAC1E,YAAoB,KAA2B;EAAV,KAAA,MAAA;CAAW;CAEhD,OAAc,YAAY,SAAkB;EAE1C,MAAM,YAAY,wBAAwB,wBAAwB,eAAe,SAAS,wBAAwB,CAAC;EACnH,oBAAoB,SAAS;EAE7B,IAAI;EACJ,IAAI,UAAU,QAAQ,OACpB,cAAc,IAAI,aAAa,SAAS;OACnC,IAAI,UAAU,QAAQ,MAAM;GACjC,IAAI,UAAU,QAAQ,SACpB,cAAc,IAAI,cAAc;IAC9B,GAAG;IACH,KAAK,UAAU;GACjB,CAAC;QACI,IAAI,UAAU,QAAQ,SAC3B,cAAc,IAAI,cAAc;IAC9B,GAAG;IACH,KAAK,UAAU;GACjB,CAAC;QACI,IAAI,UAAU,QAAQ,SAC3B,cAAc,IAAI,cAAc;IAC9B,GAAG;IACH,KAAK,UAAU;GACjB,CAAC;QACI,IAAI,UAAU,QAAQ,aAC3B,cAAc,IAAI,mBAAmB;IACnC,GAAG;IACH,KAAK,UAAU;GACjB,CAAC;QAED,MAAM,IAAI,mBACR,oBAAoB,UAAU,IAAI,cAAc,UAAU,IAAI,4BAChE;EAEJ,OAAO,IAAI,UAAU,QAAQ,WAC3B,cAAc,IAAI,iBAAiB;GACjC,GAAG;GACH,KAAK,UAAU;EACjB,CAAC;OACI,IAAI,UAAU,QAAQ,UAC3B,cAAc,IAAI,gBAAgB;GAChC,GAAG;GACH,KAAK,UAAU;EACjB,CAAC;OAED,MAAM,IAAI,mBAAmB,oBAAoB,UAAU,IAAI,4BAA4B;EAG7F,OAAO,IAAI,UAAU,WAAW;CAClC;CAMA,OAAc,cAAkD,KAAU;EACxE,OAAO,UAAU,YAAY,GAAG;CAGlC;CAEA,OAAc,EAAE,aAAa,SAAmC,CAAC,GAAe;EAC9E,IAAI,YAAY,OAAO,KAAK,IAAI;EAEhC,MAAM,EAAE,KAAK,GAAG,QAAQ,KAAK,IAAI;EACjC,OAAO;CACT;;;;;;;CAQA,IAAW,+BAA6D;EACtE,MAAM,+BAA6D,KAAK,IAAI,gCAAgC,CAAC;EAE7G,IAAI,KAAK,IAAI,IAAI,KAAK;GACpB,IAAI,CAAC,6BAA6B,SAAS,KAAK,IAAI,IAAI,GAAiC,GACvF,MAAM,IAAI,mBACR,GAAG,KAAK,wBAAwB,gBAAgB,KAAK,IAAI,IAAI,IAAI,iCACnE;GAGF,OAAO,CAAC,KAAK,IAAI,IAAI,GAAG;EAC1B;EAEA,OAAO;CACT;CAEA,IAAW,4CAA6E;EACtF,OAAO,KAAK,IAAI,6CAA6C,CAAC;CAChE;;;;CAKA,IAAW,MAAyB;EAClC,OAAO,KAAK,IAAI,IAAI;CACtB;;;;;CAMA,IAAW,QAAgB;EACzB,IAAI,KAAK,IAAI,IAAI,KAAK,OAAO,KAAK,IAAI,IAAI;EAE1C,MAAM,IAAI,mBAAmB,mCAAmC;CAClE;CAEA,IAAW,WAAoB;EAC7B,OAAO,KAAK,IAAI,IAAI,QAAQ,KAAA;CAC9B;CAEA,IAAW,MAAM,OAAe;EAC9B,KAAK,IAAI,IAAI,MAAM;CACrB;CAEA,IAAW,cAAc;EACvB,OAAO,yBAAyB,IAAI;CACtC;CAEA,IAAW,YAA8B;EACvC,OAAO,KAAK,IAAI;CAClB;;;;CAKA,IAAW,sBAAkD;EAC3D,OAAO,KAAK,IAAI;CAClB;CAEA,IAAW,WAAW;EACpB,OAAO,KAAK,IAAI;CAClB;;;;CAKA,iBAAwB,gBAA0B,WAAW;EAC3D,OAAO,uBAAuB;GAC5B,KAAK,KAAK,IAAI;GACC;EACjB,CAAC;CACH;;;;;;;CAQA,IAAW,qBAAqB;EAC9B,MAAM,MAAM,KAAK,6BAA6B;EAC9C,IAAI,CAAC,KACH,MAAM,IAAI,mBAAmB,GAAG,KAAK,wBAAwB,uCAAuC;EAGtG,OAAO;CACT;;;;CAKA,IAAW,MAA0B;EACnC,OAAO,KAAK,IAAI,IAAI;CACtB;;;;;;;;CASA,IAAW,IAAI,KAAiE;EAM9E,IAAI,CAAC,CAJH,GAAI,KAAK,IAAI,gCAAgC,CAAC,GAC9C,GAAI,KAAK,IAAI,6CAA6C,CAAC,CAGtC,CAAC,CAAC,SAAS,GAAG,GACnC,MAAM,IAAI,mBAAmB,GAAG,KAAK,wBAAwB,yBAAyB,IAAI,GAAG;EAG/F,KAAK,IAAI,IAAI,MAAM;CACrB;CAEA,kCACE,KAC6D;EAC7D,IAAI,CAAC,KAAK,6BAA6B,SAAS,GAAG,GACjD,MAAM,IAAI,mBAAmB,GAAG,KAAK,wBAAwB,mCAAmC,IAAI,GAAG;CAE3G;CAEA,OAAc,cAAiE,WAAsB;EACnG,IAAI;EAEJ,IAAI,UAAU,QAAQ,OACpB,cAAc,aAAa,cAAc,SAAS;OAC7C,IAAI,UAAU,QAAQ,MAAM;GACjC,IAAI,UAAU,QAAQ,SACpB,cAAc,cAAc,cAAc,UAAU,SAAS;QACxD,IAAI,UAAU,QAAQ,SAC3B,cAAc,cAAc,cAAc,UAAU,SAAS;QACxD,IAAI,UAAU,QAAQ,SAC3B,cAAc,cAAc,cAAc,UAAU,SAAS;QACxD,IAAI,UAAU,QAAQ,aAC3B,cAAc,mBAAmB,cAAc,UAAU,SAAS;QAElE,MAAM,IAAI,mBAER,oBAAoB,UAAU,IAAI,cAAc,UAAU,IAAI,sDAChE;EAEJ,OAAO,IAAI,UAAU,QAAQ,UAC3B,cAAc,gBAAgB,cAAc,UAAU,SAAS;OAC1D,IAAI,UAAU,QAAQ,WAC3B,cAAc,iBAAiB,cAAc,UAAU,SAAS;OAEhE,MAAM,IAAI,mBAER,oBAAoB,UAAU,IAAI,sDACpC;EAGF,OAAO,IAAI,UAAU,WAAW;CAClC;;;;CAKA,IAAW,cAAc;EACvB,MAAM,cAAc,cAAc,OAAO,KAAK,IAAI,gBAAgB;EAClE,MAAM,oBAAoB,IAAI,WAAW,CAAC,GAAG,aAAa,GAAG,KAAK,IAAI,UAAU,CAAC;EAEjF,OAAO,IAAI,kBAAkB,SAAS,iBAAiB;CACzD;;;;CAKA,OAAc,gBAAgB,aAAqB;EACjD,MAAM,EAAE,SAAS,iBAAiB,OAAO,WAAW;EACpD,MAAM,CAAC,MAAM,cAAc,cAAc,OAAO,IAAI;EACpD,MAAM,YAAY,KAAK,MAAM,UAAU;EAEvC,MAAM,iBAAiB,oBAAoB,MAAM,aAAa,SAAS,qBAAqB,IAAI;EAChG,IAAI,CAAC,gBACH,MAAM,IAAI,mBAAmB,kDAAkD,KAAK,EAAE;EAGxF,MAAM,MAAM,eAAe,eAAe,SAAS;EACnD,OAAO,IAAI,UAAU,GAAG;CAC1B;;;;CAKA,GAKE,UACA,UACA,UAC6D;EAE7D,OADc;GAAC;GAAU;GAAU;EAAQ,CAAC,CAAC,OAAO,OACzC,CAAC,CAAC,MAAM,SAAS,KAAK,IAAI,gBAAgB,IAAI;CAC3D;;;;;;CAOA,UACE,MACmE;EACnE,IAAI,CAAC,KAAK,GAAG,gBAAgB,KAAK,SAAS,iBACzC,MAAM,IAAI,mBAAmB,kEAAkE;EAGjG,OAAO,UAAU,cAAc,KAAK,IAAI,kBAAkB,CAAC;CAG7D;;;;;;CAOA,OAAc,OAAkB;EAC9B,OAAO,2BAA2B,KAAK,OAAO,GAAG,MAAM,OAAO,CAAC;CACjE;;;;;CAMA,IAAW,0BAA0B;EACnC,OAAO,uBAAuB,KAAK,OAAO,CAAC;CAC7C;CAEA,OAAc,6CAA6C,KAA0D;EACnH,MAAM,0BAA0B,oBAAoB,MAAM,aACxD,SAAS,6BAA6B,SAAS,GAAG,CACpD;EAEA,IAAI,CAAC,yBACH,MAAM,IAAI,WAAW,2DAA2D,IAAI,EAAE;EAGxF,OAAO;CACT;AACF"}