@animo-id/pex
Version:
A Typescript implementation of the v1 and v2 DIF Presentation Exchange specification
437 lines • 49.7 kB
JavaScript
;
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.PEX = void 0;
const ConstraintUtils_1 = require("./ConstraintUtils");
const evaluation_1 = require("./evaluation");
const signing_1 = require("./signing");
const types_1 = require("./types");
const PexCredentialMapper_1 = require("./types/PexCredentialMapper");
const utils_1 = require("./utils");
const validation_1 = require("./validation");
/**
* This is the main interfacing class to be used by developers using the PEX library.
*/
class PEX {
constructor(options) {
// TODO: So we have state in the form of this property which is set in the constructor, but we are overwriting it elsewhere. We need to retrhink how to instantiate PEX
this._evaluationClientWrapper = new evaluation_1.EvaluationClientWrapper();
this.options = options;
}
/***
* The evaluatePresentation compares what is expected from one or more presentations with a presentationDefinition.
* presentationDefinition: It can be either v1 or v2 of presentationDefinition
*
* @param presentationDefinition the definition of what is expected in the presentation.
* @param presentations the presentation(s) which have to be evaluated in comparison of the definition.
* @param opts - limitDisclosureSignatureSuites the credential signature suites that support limit disclosure
*
* @return the evaluation results specify what was expected and was fulfilled and also specifies which requirements described in the input descriptors
* were not fulfilled by the presentation(s).
*/
evaluatePresentation(presentationDefinition, presentations, opts) {
var _a;
// We map it to an array for now to make processing on the presentations easier, but before checking against the submission
// we will transform it to the original structure (array vs single) so the references in the submission stay correct
const presentationsArray = Array.isArray(presentations) ? presentations : [presentations];
if (presentationsArray.length === 0) {
throw new Error('At least one presentation must be provided');
}
let originalPresentationSubmission = opts === null || opts === void 0 ? void 0 : opts.presentationSubmission;
const generatePresentationSubmission = (opts === null || opts === void 0 ? void 0 : opts.generatePresentationSubmission) !== undefined ? opts.generatePresentationSubmission : (opts === null || opts === void 0 ? void 0 : opts.presentationSubmission) === undefined;
const pd = types_1.SSITypesBuilder.toInternalPresentationDefinition(presentationDefinition);
const presentationsCopy = JSON.parse(JSON.stringify(presentationsArray));
const wrappedPresentations = presentationsCopy.map((p) => types_1.SSITypesBuilder.mapExternalVerifiablePresentationToWrappedVP(p));
let presentationSubmission = opts === null || opts === void 0 ? void 0 : opts.presentationSubmission;
let presentationSubmissionLocation = (_a = opts === null || opts === void 0 ? void 0 : opts.presentationSubmissionLocation) !== null && _a !== void 0 ? _a : ((Array.isArray(presentations) && presentations.length > 1) || !PexCredentialMapper_1.PexCredentialMapper.isW3cPresentation(wrappedPresentations[0].presentation)
? signing_1.PresentationSubmissionLocation.EXTERNAL
: signing_1.PresentationSubmissionLocation.PRESENTATION);
// When only one presentation, we also allow it to be present in the VP
if (!presentationSubmission &&
presentationsArray.length === 1 &&
PexCredentialMapper_1.PexCredentialMapper.isW3cPresentation(wrappedPresentations[0].presentation) &&
!(opts === null || opts === void 0 ? void 0 : opts.generatePresentationSubmission)) {
const decoded = wrappedPresentations[0].decoded;
if ('presentation_submission' in decoded) {
presentationSubmission = JSON.parse(JSON.stringify(decoded.presentation_submission));
originalPresentationSubmission = decoded.presentation_submission;
}
if (!presentationSubmission) {
throw Error(`Either a presentation submission as part of the VP or provided in options was expected`);
}
presentationSubmissionLocation = signing_1.PresentationSubmissionLocation.PRESENTATION;
if ((opts === null || opts === void 0 ? void 0 : opts.presentationSubmissionLocation) && opts.presentationSubmissionLocation !== signing_1.PresentationSubmissionLocation.PRESENTATION) {
throw new Error(`unexpected presentationSubmissionLocation ${opts.presentationSubmissionLocation} was provided. Expected ${signing_1.PresentationSubmissionLocation.PRESENTATION} when no presentationSubmission passed and first verifiable presentation contains a presentation_submission and generatePresentationSubmission is false`);
}
// We need to update the vp path as PEX decoded assumes it's an external submission
// So we need to update the submission paths
if (wrappedPresentations[0].format === 'jwt_vp') {
for (const descriptor of presentationSubmission.descriptor_map) {
if (!descriptor.path.startsWith('$.vp')) {
descriptor.path = descriptor.path.replace('$.', '$.vp.');
}
}
}
}
else if (!presentationSubmission && !generatePresentationSubmission) {
throw new Error('Presentation submission in options was expected.');
}
// TODO: we should probably add support for holder dids in the kb-jwt of an SD-JWT. We can extract this from the
// `wrappedPresentation.original.compactKbJwt`, but as HAIP doesn't use dids, we'll leave it for now.
const holderDIDs = wrappedPresentations
.map((p) => {
return PexCredentialMapper_1.PexCredentialMapper.isW3cPresentation(p.presentation) && p.presentation.holder ? p.presentation.holder : undefined;
})
.filter((d) => d !== undefined);
const updatedOpts = Object.assign(Object.assign({}, opts), { holderDIDs,
presentationSubmission,
presentationSubmissionLocation,
generatePresentationSubmission });
const allWvcs = wrappedPresentations.reduce((all, wvp) => [...all, ...wvp.vcs], []);
const result = this._evaluationClientWrapper.evaluatePresentations(pd, Array.isArray(presentations) ? wrappedPresentations : wrappedPresentations[0], updatedOpts);
if (result.areRequiredCredentialsPresent !== ConstraintUtils_1.Status.ERROR) {
const selectFromClientWrapper = new evaluation_1.EvaluationClientWrapper();
const selectResults = selectFromClientWrapper.selectFrom(pd, allWvcs, updatedOpts);
if (selectResults.areRequiredCredentialsPresent !== ConstraintUtils_1.Status.ERROR) {
result.errors = [];
}
}
return Object.assign(Object.assign({}, result), { value: originalPresentationSubmission !== null && originalPresentationSubmission !== void 0 ? originalPresentationSubmission : result.value });
}
/***
* The evaluate compares what is expected from a verifiableCredentials with the presentationDefinition.
*
* @param presentationDefinition the v1 or v2 definition of what is expected in the presentation.
* @param verifiableCredentials the verifiable credentials which are candidates to fulfill requirements defined in the presentationDefinition param.
* @param opts - holderDIDs the list of the DIDs that the wallet holders controls. Optional, but needed by some input requirements that do a holderDID check.
* @ - limitDisclosureSignatureSuites the credential signature suites that support limit disclosure
*
* @return the evaluation results specify what was expected and was fulfilled and also specifies which requirements described in the input descriptors
* were not fulfilled by the verifiable credentials.
*/
evaluateCredentials(presentationDefinition, verifiableCredentials, opts) {
const wrappedVerifiableCredentials = types_1.SSITypesBuilder.mapExternalVerifiableCredentialsToWrappedVcs(verifiableCredentials);
// TODO: So we have state in the form of this property which is set in the constructor, but we are overwriting it here. We need to retrhink how to instantiate PEX
this._evaluationClientWrapper = new evaluation_1.EvaluationClientWrapper();
const pd = types_1.SSITypesBuilder.toInternalPresentationDefinition(presentationDefinition);
const result = this._evaluationClientWrapper.evaluate(pd, wrappedVerifiableCredentials, opts);
if (result.value && result.value.descriptor_map.length) {
const selectFromClientWrapper = new evaluation_1.EvaluationClientWrapper();
const selectResults = selectFromClientWrapper.selectFrom(pd, wrappedVerifiableCredentials, opts);
result.areRequiredCredentialsPresent = selectResults.areRequiredCredentialsPresent;
result.errors = selectResults.errors;
}
else {
result.areRequiredCredentialsPresent = ConstraintUtils_1.Status.ERROR;
}
return result;
}
/**
* The selectFrom method is a helper function that helps filter out the verifiable credentials which can not be selected and returns
* the selectable credentials.
*
* @param presentationDefinition the v1 or v2 definition of what is expected in the presentation.
* @param verifiableCredentials verifiable credentials are the credentials from wallet provided to the library to find selectable credentials.
* @param opts - holderDIDs the decentralized identifier(s) of the wallet holderDID. This is used to identify the credentials issued to the holderDID of wallet in certain scenario's.
* - limitDisclosureSignatureSuites the credential signature suites that support limit disclosure
*
* @return the selectable credentials.
*/
selectFrom(presentationDefinition, verifiableCredentials, opts) {
const verifiableCredentialCopy = JSON.parse(JSON.stringify(verifiableCredentials));
const pd = types_1.SSITypesBuilder.toInternalPresentationDefinition(presentationDefinition);
// TODO: So we have state in the form of this property which is set in the constructor, but we are overwriting it here. We need to retrhink how to instantiate PEX
this._evaluationClientWrapper = new evaluation_1.EvaluationClientWrapper();
return this._evaluationClientWrapper.selectFrom(pd, types_1.SSITypesBuilder.mapExternalVerifiableCredentialsToWrappedVcs(verifiableCredentialCopy), opts);
}
presentationSubmissionFrom(presentationDefinition, selectedCredentials, opts) {
const pd = types_1.SSITypesBuilder.toInternalPresentationDefinition(presentationDefinition);
return this._evaluationClientWrapper.submissionFrom(pd, types_1.SSITypesBuilder.mapExternalVerifiableCredentialsToWrappedVcs(selectedCredentials), opts);
}
/**
* This method helps create an Unsigned Presentation. An Unsigned Presentation after signing becomes a Presentation. And can be sent to
* the verifier after signing it.
*
* @param presentationDefinition the v1 or v2 definition of what is expected in the presentation.
* @param selectedCredentials the credentials which were declared selectable by getSelectableCredentials and then chosen by the intelligent-user
* (e.g. human).
* @param opts - holderDID optional; the decentralized identity of the wallet holderDID. This is used to identify the holderDID of the presentation.
*
* @return the presentation.
*/
presentationFrom(presentationDefinition, selectedCredentials, opts) {
var _a;
const presentationSubmission = this.presentationSubmissionFrom(presentationDefinition, selectedCredentials, opts);
const hasSdJwtCredentials = selectedCredentials.some((c) => PexCredentialMapper_1.PexCredentialMapper.isSdJwtDecodedCredential(c) || PexCredentialMapper_1.PexCredentialMapper.isSdJwtEncoded(c));
// We could include it in the KB-JWT? Not sure if we want that
if ((opts === null || opts === void 0 ? void 0 : opts.presentationSubmissionLocation) === signing_1.PresentationSubmissionLocation.PRESENTATION && hasSdJwtCredentials) {
throw new Error('Presentation submission location cannot be set to presentation when creating a presentation with an SD-JWT VC');
}
const presentationSubmissionLocation = (_a = opts === null || opts === void 0 ? void 0 : opts.presentationSubmissionLocation) !== null && _a !== void 0 ? _a : (hasSdJwtCredentials ? signing_1.PresentationSubmissionLocation.EXTERNAL : signing_1.PresentationSubmissionLocation.PRESENTATION);
const presentations = this.constructPresentations(selectedCredentials, Object.assign(Object.assign({}, opts), {
// We only pass in the submission in case it needs to be included in the presentation
presentationSubmission: presentationSubmissionLocation === signing_1.PresentationSubmissionLocation.PRESENTATION ? presentationSubmission : undefined }));
this.updateSdJwtCredentials(presentations);
return {
presentations,
presentationSubmissionLocation,
presentationSubmission,
};
}
constructPresentations(selectedCredentials, opts) {
var _a, _b, _c;
if (!selectedCredentials) {
throw Error(`At least a verifiable credential needs to be passed in to create a presentation`);
}
const verifiableCredential = (Array.isArray(selectedCredentials) ? selectedCredentials : [selectedCredentials]);
const wVCs = verifiableCredential.map((vc) => PexCredentialMapper_1.PexCredentialMapper.toWrappedVerifiableCredential(vc));
const holders = Array.from(new Set(wVCs.flatMap((wvc) => (0, utils_1.getSubjectIdsAsString)(wvc.credential))));
const holder = (_a = opts === null || opts === void 0 ? void 0 : opts.holderDID) !== null && _a !== void 0 ? _a : (holders.length === 1 ? holders[0] : undefined);
const type = ((_b = opts === null || opts === void 0 ? void 0 : opts.basePresentationPayload) === null || _b === void 0 ? void 0 : _b.type)
? Array.isArray(opts.basePresentationPayload.type)
? opts.basePresentationPayload.type
: [opts.basePresentationPayload.type]
: [];
if (!type.includes('VerifiablePresentation')) {
type.push('VerifiablePresentation');
}
const context = ((_c = opts === null || opts === void 0 ? void 0 : opts.basePresentationPayload) === null || _c === void 0 ? void 0 : _c['@context'])
? Array.isArray(opts.basePresentationPayload['@context'])
? opts.basePresentationPayload['@context']
: [opts.basePresentationPayload['@context']]
: [];
if (!context.includes('https://www.w3.org/2018/credentials/v1')) {
context.push('https://www.w3.org/2018/credentials/v1');
}
if (opts === null || opts === void 0 ? void 0 : opts.presentationSubmission) {
if (!type.includes('PresentationSubmission')) {
type.push('PresentationSubmission');
}
if (!context.includes('https://identity.foundation/presentation-exchange/submission/v1')) {
context.push('https://identity.foundation/presentation-exchange/submission/v1');
}
}
const result = [];
if (PEX.allowMultipleVCsPerPresentation(verifiableCredential)) {
result.push(Object.assign(Object.assign(Object.assign(Object.assign({}, opts === null || opts === void 0 ? void 0 : opts.basePresentationPayload), { '@context': context, type,
holder }), (!!(opts === null || opts === void 0 ? void 0 : opts.presentationSubmission) && { presentation_submission: opts.presentationSubmission })), { verifiableCredential }));
}
else {
verifiableCredential.forEach((vc) => {
if (PexCredentialMapper_1.PexCredentialMapper.isSdJwtDecodedCredential(vc)) {
result.push(vc);
}
else if (PexCredentialMapper_1.PexCredentialMapper.isSdJwtEncoded(vc)) {
const decoded = PexCredentialMapper_1.PexCredentialMapper.decodeVerifiableCredential(vc);
result.push(decoded);
}
else {
// This should be jwt or json-ld
result.push(Object.assign(Object.assign(Object.assign(Object.assign({}, opts === null || opts === void 0 ? void 0 : opts.basePresentationPayload), { '@context': context, type,
holder }), (!!(opts === null || opts === void 0 ? void 0 : opts.presentationSubmission) && { presentation_submission: opts.presentationSubmission })), { verifiableCredential: [vc] }));
}
});
}
return result;
}
/*
TODO SDK-37 refinement needed
*/
static allowMultipleVCsPerPresentation(verifiableCredentials) {
var _a;
const jwtCredentials = verifiableCredentials.filter((c) => PexCredentialMapper_1.PexCredentialMapper.isJwtEncoded(c) || PexCredentialMapper_1.PexCredentialMapper.isJwtDecodedCredential(c));
if (jwtCredentials.length > 0) {
const subjects = new Set();
const verificationMethods = new Set();
for (const credential of jwtCredentials) {
const decodedCredential = PexCredentialMapper_1.PexCredentialMapper.isJwtEncoded(credential)
? PexCredentialMapper_1.PexCredentialMapper.decodeVerifiableCredential(credential)
: credential;
const subject = decodedCredential.sub ||
(decodedCredential.vc && 'id' in decodedCredential.vc.credentialSubject && decodedCredential.vc.credentialSubject.id);
if (subject) {
subjects.add(subject);
}
const vcProof = (_a = decodedCredential.proof) !== null && _a !== void 0 ? _a : decodedCredential.vc.proof;
const proofs = Array.isArray(vcProof) ? vcProof : [vcProof];
proofs.filter((proof) => proof.verificationMethod).forEach((proof) => verificationMethods.add(proof.verificationMethod));
}
// If there's more than one unique subject or verification method, we can't allow multiple VCs in a single presentation
if (subjects.size > 1 || verificationMethods.size > 1) {
return false;
}
}
if (verifiableCredentials.some((c) => PexCredentialMapper_1.PexCredentialMapper.isSdJwtEncoded(c) || PexCredentialMapper_1.PexCredentialMapper.isSdJwtDecodedCredential(c))) {
return false;
}
return true;
}
/**
* This method validates whether an object is usable as a presentation definition or not.
*
* @param presentationDefinition presentationDefinition of V1 or v2 to be validated.
*
* @return the validation results to reveal what is acceptable/unacceptable about the passed object to be considered a valid presentation definition
*/
static validateDefinition(presentationDefinition) {
const result = (0, utils_1.definitionVersionDiscovery)(presentationDefinition);
if (result.error) {
const errorParts = [result.error];
const v1ErrorString = (0, utils_1.formatValidationErrors)(result.v1Errors);
if (v1ErrorString) {
errorParts.push('\nVersion 1 validation errors:\n ' + v1ErrorString);
}
const v2ErrorString = (0, utils_1.formatValidationErrors)(result.v2Errors);
if (v2ErrorString) {
errorParts.push('\nVersion 2 validation errors:\n ' + v2ErrorString);
}
throw new Error(errorParts.join(''));
}
const validators = [];
result.version === types_1.PEVersion.v1
? validators.push({
bundler: new validation_1.PresentationDefinitionV1VB('root'),
target: types_1.SSITypesBuilder.modelEntityToInternalPresentationDefinitionV1(presentationDefinition),
})
: validators.push({
bundler: new validation_1.PresentationDefinitionV2VB('root'),
target: types_1.SSITypesBuilder.modelEntityInternalPresentationDefinitionV2(presentationDefinition),
});
return new validation_1.ValidationEngine().validate(validators);
}
/**
* This method validates whether an object is usable as a presentation submission or not.
*
* @param presentationSubmission the object to be validated.
*
* @return the validation results to reveal what is acceptable/unacceptable about the passed object to be considered a valid presentation submission
*/
static validateSubmission(presentationSubmission) {
return new validation_1.ValidationEngine().validate([
{
bundler: new validation_1.PresentationSubmissionVB('root'),
target: presentationSubmission,
},
]);
}
/**
* This method can be used to combine a definition, selected Verifiable Credentials, together with
* signing opts and a callback to sign a presentation, making it a Verifiable Presentation before sending.
*
* Please note that PEX has no signature support on purpose. We didn't want this library to depend on all kinds of signature suites.
* The callback function next to the Signing Params also gets a Presentation which is evaluated against the definition.
* It is up to you to decide whether you simply update the supplied partial proof and add it to the presentation in the callback,
* or whether you will use the selected Credentials, Presentation definition, evaluation results and/or presentation submission together with the signature opts
*
* @param presentationDefinition the Presentation Definition V1 or V2
* @param selectedCredentials the PEX and/or User selected/filtered credentials that will become part of the Verifiable Presentation
* @param signingCallBack the function which will be provided as a parameter. And this will be the method that will be able to perform actual
* signing. One example of signing is available in the project named. pe-selective-disclosure.
* @param opts Signing Params these are the signing params required to sign.
*
* @return the signed and thus Verifiable Presentation.
*/
verifiablePresentationFrom(presentationDefinition, selectedCredentials, signingCallBack, opts) {
return __awaiter(this, void 0, void 0, function* () {
const { holderDID, signatureOptions, proofOptions } = opts;
function limitedDisclosureSuites() {
let limitDisclosureSignatureSuites = [];
if (proofOptions === null || proofOptions === void 0 ? void 0 : proofOptions.typeSupportsSelectiveDisclosure) {
if (!(proofOptions === null || proofOptions === void 0 ? void 0 : proofOptions.type)) {
throw Error('Please provide a proof type if you enable selective disclosure');
}
limitDisclosureSignatureSuites = [proofOptions.type];
}
return limitDisclosureSignatureSuites;
}
const holderDIDs = holderDID ? [holderDID] : [];
const limitDisclosureSignatureSuites = limitedDisclosureSuites();
const evaluationResult = this.evaluateCredentials(presentationDefinition, selectedCredentials, {
holderDIDs,
limitDisclosureSignatureSuites,
});
if (evaluationResult.areRequiredCredentialsPresent === ConstraintUtils_1.Status.ERROR) {
throw new Error('Could not create presentation, selected credentials do not satisfy the presentation definition');
}
const presentationResult = this.presentationFrom(presentationDefinition, evaluationResult.verifiableCredential, opts);
const presentations = presentationResult.presentations;
const evaluationResults = this.evaluatePresentation(presentationDefinition, presentations, Object.assign({ limitDisclosureSignatureSuites }, (presentationResult.presentationSubmissionLocation === signing_1.PresentationSubmissionLocation.EXTERNAL && {
presentationSubmission: presentationResult.presentationSubmission,
})));
if (!evaluationResults.value && selectedCredentials.length === 0) {
evaluationResults.value = presentationResult.presentationSubmission;
}
if (!evaluationResults.value) {
throw new Error('Could not get evaluation results from presentationResult');
}
const proof = {
type: proofOptions === null || proofOptions === void 0 ? void 0 : proofOptions.type,
verificationMethod: signatureOptions === null || signatureOptions === void 0 ? void 0 : signatureOptions.verificationMethod,
created: (proofOptions === null || proofOptions === void 0 ? void 0 : proofOptions.created) ? proofOptions.created : new Date().toISOString(),
proofPurpose: proofOptions === null || proofOptions === void 0 ? void 0 : proofOptions.proofPurpose,
proofValue: signatureOptions === null || signatureOptions === void 0 ? void 0 : signatureOptions.proofValue,
jws: signatureOptions === null || signatureOptions === void 0 ? void 0 : signatureOptions.jws,
challenge: proofOptions === null || proofOptions === void 0 ? void 0 : proofOptions.challenge,
nonce: proofOptions === null || proofOptions === void 0 ? void 0 : proofOptions.nonce,
domain: proofOptions === null || proofOptions === void 0 ? void 0 : proofOptions.domain,
};
this.updateSdJwtCredentials(presentations, proofOptions === null || proofOptions === void 0 ? void 0 : proofOptions.nonce);
const verifiablePresentations = [];
for (const presentation of presentations) {
const callBackParams = {
options: Object.assign(Object.assign({}, opts), { presentationSubmissionLocation: presentationResult.presentationSubmissionLocation }),
presentation,
presentationDefinition,
selectedCredentials,
proof,
presentationSubmission: evaluationResults.value,
evaluationResults,
};
verifiablePresentations.push(yield signingCallBack(callBackParams));
}
return {
verifiablePresentations,
presentationSubmissionLocation: presentationResult.presentationSubmissionLocation,
presentationSubmission: evaluationResults.value,
};
});
}
updateSdJwtCredentials(presentations, nonce) {
presentations.forEach((presentation, index) => {
var _a;
// Select type without kbJwt as isSdJwtDecodedCredential and won't accept the partial sdvc type
if (PexCredentialMapper_1.PexCredentialMapper.isSdJwtDecodedCredential(presentation)) {
const sdJwtCredential = presentation;
// extract sd_alg or default to sha-256
const hashAlg = (_a = sdJwtCredential.signedPayload._sd_alg) !== null && _a !== void 0 ? _a : 'sha-256';
const sdHash = (0, utils_1.calculateSdHash)(sdJwtCredential.compactSdJwtVc, hashAlg);
const kbJwt = {
// alg MUST be set by the signer
header: {
typ: 'kb+jwt',
},
// aud MUST be set by the signer or provided by e.g. SIOP/OpenID4VP lib
payload: {
iat: Math.floor(new Date().getTime() / 1000),
nonce: nonce,
sd_hash: sdHash,
},
};
presentations[index] = Object.assign(Object.assign({}, sdJwtCredential), { kbJwt });
}
});
}
static definitionVersionDiscovery(presentationDefinition) {
return (0, utils_1.definitionVersionDiscovery)(presentationDefinition);
}
}
exports.PEX = PEX;
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiUEVYLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vbGliL1BFWC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7Ozs7Ozs7Ozs7QUFjQSx1REFBMkM7QUFDM0MsNkNBQXdIO0FBQ3hILHVDQVNtQjtBQUNuQixtQ0FBMkk7QUFDM0kscUVBTXFDO0FBQ3JDLG1DQUFxSDtBQUNySCw2Q0FBNkk7QUFjN0k7O0dBRUc7QUFDSCxNQUFhLEdBQUc7SUFJZCxZQUFZLE9BQW9CO1FBQzlCLHdLQUF3SztRQUN4SyxJQUFJLENBQUMsd0JBQXdCLEdBQUcsSUFBSSxvQ0FBdUIsRUFBRSxDQUFDO1FBRTlELElBQUksQ0FBQyxPQUFPLEdBQUcsT0FBTyxDQUFDO0lBQ3pCLENBQUM7SUFFRDs7Ozs7Ozs7OztPQVVHO0lBQ0ksb0JBQW9CLENBQ3pCLHNCQUErQyxFQUMvQyxhQUFnSCxFQUNoSCxJQVlDOztRQUVELDJIQUEySDtRQUMzSCxvSEFBb0g7UUFDcEgsTUFBTSxrQkFBa0IsR0FBRyxLQUFLLENBQUMsT0FBTyxDQUFDLGFBQWEsQ0FBQyxDQUFDLENBQUMsQ0FBQyxhQUFhLENBQUMsQ0FBQyxDQUFDLENBQUMsYUFBYSxDQUFDLENBQUM7UUFDMUYsSUFBSSxrQkFBa0IsQ0FBQyxNQUFNLEtBQUssQ0FBQyxFQUFFLENBQUM7WUFDcEMsTUFBTSxJQUFJLEtBQUssQ0FBQyw0Q0FBNEMsQ0FBQyxDQUFDO1FBQ2hFLENBQUM7UUFFRCxJQUFJLDhCQUE4QixHQUFHLElBQUksYUFBSixJQUFJLHVCQUFKLElBQUksQ0FBRSxzQkFBc0IsQ0FBQztRQUNsRSxNQUFNLDhCQUE4QixHQUNsQyxDQUFBLElBQUksYUFBSixJQUFJLHVCQUFKLElBQUksQ0FBRSw4QkFBOEIsTUFBSyxTQUFTLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQyw4QkFBOEIsQ0FBQyxDQUFDLENBQUMsQ0FBQSxJQUFJLGFBQUosSUFBSSx1QkFBSixJQUFJLENBQUUsc0JBQXNCLE1BQUssU0FB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